The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
Changes 046
MANIFEST 2320
META.yml 12
Makefile.PL 01
README.pod 52
examples/microhttpd.pl 11
lib/Mojo/Asset/File.pm 30
lib/Mojo/Base.pm 01
lib/Mojo/Client.pm 812
lib/Mojo/Command/Cgi.pm 890
lib/Mojo/Command/Daemon.pm 1170
lib/Mojo/Command/Fastcgi.pm 800
lib/Mojo/Command/Get.pm 1230
lib/Mojo/Command/Psgi.pm 840
lib/Mojo/Command/Test.pm 1220
lib/Mojo/Command/Version.pm 1350
lib/Mojo/Command.pm 8200
lib/Mojo/Commands.pm 3090
lib/Mojo/Content.pm 103
lib/Mojo/DOM.pm 22
lib/Mojo/IOLoop.pm 140201
lib/Mojo/Loader.pm 1130
lib/Mojo/Message.pm 3615
lib/Mojo/Parameters.pm 67
lib/Mojo/Server/CGI.pm 01
lib/Mojo/Server/Daemon.pm 912
lib/Mojo/Server/FastCGI.pm 01
lib/Mojo/Server/PSGI.pm 01
lib/Mojo/Transaction/HTTP.pm 22
lib/Mojo/URL.pm 42
lib/Mojo.pm 220
lib/MojoX/Controller.pm 1440
lib/MojoX/Dispatcher/Routes/Controller.pm 660
lib/MojoX/Dispatcher/Routes.pm 4220
lib/MojoX/Dispatcher/Static.pm 3610
lib/MojoX/Renderer.pm 5140
lib/MojoX/Routes/Match.pm 3270
lib/MojoX/Routes/Pattern.pm 4560
lib/MojoX/Routes.pm 5100
lib/MojoX/Session/Cookie/Controller.pm 2540
lib/MojoX/Session/Cookie.pm 1630
lib/MojoX/Types.pm 1010
lib/Mojolicious/Command/Cgi.pm 089
lib/Mojolicious/Command/Daemon.pm 0117
lib/Mojolicious/Command/Fastcgi.pm 081
lib/Mojolicious/Command/Generate.pm 33
lib/Mojolicious/Command/Get.pm 0126
lib/Mojolicious/Command/Inflate.pm 35
lib/Mojolicious/Command/Psgi.pm 085
lib/Mojolicious/Command/Test.pm 0122
lib/Mojolicious/Command/Version.pm 0130
lib/Mojolicious/Commands.pm 962
lib/Mojolicious/Controller.pm 20371
lib/Mojolicious/Guides/Cheatsheet.pod 220
lib/Mojolicious/Guides/FAQ.pod 46
lib/Mojolicious/Guides/Rendering.pod 035
lib/Mojolicious/Guides/Routing.pod 132
lib/Mojolicious/Lite.pm 6917
lib/Mojolicious/Plugin/AgentCondition.pm 02
lib/Mojolicious/Plugin/DefaultHelpers.pm 02
lib/Mojolicious/Plugin/EpRenderer.pm 02
lib/Mojolicious/Plugin/EplRenderer.pm 02
lib/Mojolicious/Plugin/PoweredBy.pm 02
lib/Mojolicious/Plugin/RequestTimer.pm 02
lib/Mojolicious/Plugin/TagHelpers.pm 2947
lib/Mojolicious/Renderer.pm 0506
lib/Mojolicious/Routes/Match.pm 0322
lib/Mojolicious/Routes/Pattern.pm 0453
lib/Mojolicious/Routes.pm 0986
lib/Mojolicious/Session.pm 0156
lib/Mojolicious/Static.pm 0348
lib/Mojolicious/Types.pm 092
lib/Mojolicious.pm 3584
t/mojo/apache_cgi.t 22
t/mojo/apache_fastcgi.t 22
t/mojo/app.t 22
t/mojo/client.t 56
t/mojo/client_online.t 185
t/mojo/ioloop_online.t 0131
t/mojo/ioloop_tls.t 044
t/mojo/loader.t 22
t/mojo/message.t 56
t/mojo/parameters.t 29
t/mojo/psgi.t 22
t/mojo/url.t 513
t/mojolicious/app.t 22
t/mojolicious/charset_lite_app.t 22
t/mojolicious/dispatch.t 0187
t/mojolicious/dispatcher_lite_app.t 22
t/mojolicious/embedded_lite_app.t 22
t/mojolicious/external_lite_app.t 22
t/mojolicious/i18n_lite_app.t 22
t/mojolicious/json_config_lite_app.t 22
t/mojolicious/json_config_mode_lite_app.t 22
t/mojolicious/lite_app.t 2339
t/mojolicious/longpolling_lite_app.t 7100
t/mojolicious/pattern.t 0101
t/mojolicious/pod_renderer_lite_app.t 22
t/mojolicious/production_app.t 22
t/mojolicious/renderer.t 047
t/mojolicious/routes.t 0515
t/mojolicious/twinkle_lite_app.t 22
t/mojolicious/upload_lite_app.t 22
t/mojolicious/websocket_lite_app.t 22
t/mojolicious/websocket_proxy_lite_app.t 22
t/mojolicious/websocket_tls_proxy_lite_app.t 33
t/mojox/dispatcher.t 1560
t/mojox/pattern.t 990
t/mojox/renderer.t 470
t/mojox/routes.t 5150
110 files changed (This is a version diff) 57866093
@@ -1,5 +1,51 @@
 This file documents the revision history for Perl extension Mojolicious.
 
+0.999941 2010-11-19 00:00:00
+        - Allow Mojolicious::Lite style routes in Mojolicious.
+        - Added base_tag helper to Mojolicious::Plugin::TagHelpers.
+        - Added CNAME and NS record type support to the Mojo::IOLoop
+          resolver.
+        - Improved Mojo::IOLoop responsiveness.
+        - Made Mojo::IOLoop resolver results more useful.
+
+0.999940 2010-11-15 00:00:00
+        - Improved resolver tests.
+        - Fixed IO::Socket::SSL 1.34 compatibility.
+
+0.999939 2010-11-15 00:00:00
+        - Removed IPv6 support until Perl itself gets better support for it.
+        - Added MX and PTR record type support to the Mojo::IOLoop resolver.
+          (und3f)
+        - Fixed a Mojolicious::Static rendering bug.
+        - Fixed a bug that forced connect in Mojo::IOLoop to block.
+        - Fixed a bug that prevented on_finish to be triggered for
+          interrupted connections.
+        - Fixed a TLS accept bug in Mojo::IOLoop.
+        - Fixed a small bug in Mojo::Parameters. (spleenjack)
+        - Fixed javascript and stylesheet helper. (sshaw)
+        - Fixed IPv4 address detection bug in Mojo::URL.
+        - Fixed a Mojo::Client bug where interrupted transactions were still
+          successful.
+        - Fixed a small reloader bug.
+        - Fixed typos.
+
+0.999938 2010-11-09 00:00:00
+        - Moved all commands into the Mojolicious namespace.
+        - Fixed typo.
+        - Removed OS X resource fork files.
+
+0.999937 2010-11-09 00:00:00
+        - Deprecated the MojoX namespace and merged affected modules into the
+          Mojolicious namespace, this will make reference documentation a lot
+          more accessible.
+        - Added important module overview to Mojolicious. (rhaen)
+        - Improved Mojo::Loader to allow Mojolicious recovering from tricky
+          syntax errors in Controllers.
+        - Improved param method in MojoX::Dispatcher::Routes::Controller.
+        - Fixed escaping in Mojo::Parameters to work better in the real
+          world.
+        - Fixed a small inflate command bug.
+
 0.999936 2010-11-03 00:00:00
         - Improved Mojo::Template performance slightly. (kimoto)
         - Fixed a serious WebSocket bug.
@@ -11,14 +11,6 @@ lib/Mojo/Base.pm
 lib/Mojo/ByteStream.pm
 lib/Mojo/Client.pm
 lib/Mojo/Command.pm
-lib/Mojo/Command/Cgi.pm
-lib/Mojo/Command/Daemon.pm
-lib/Mojo/Command/Fastcgi.pm
-lib/Mojo/Command/Get.pm
-lib/Mojo/Command/Psgi.pm
-lib/Mojo/Command/Test.pm
-lib/Mojo/Command/Version.pm
-lib/Mojo/Commands.pm
 lib/Mojo/Content.pm
 lib/Mojo/Content/MultiPart.pm
 lib/Mojo/Content/Single.pm
@@ -54,13 +46,20 @@ lib/Mojo/Upload.pm
 lib/Mojo/URL.pm
 lib/Mojo/Util.pm
 lib/Mojolicious.pm
+lib/Mojolicious/Command/Cgi.pm
+lib/Mojolicious/Command/Daemon.pm
+lib/Mojolicious/Command/Fastcgi.pm
 lib/Mojolicious/Command/Generate.pm
 lib/Mojolicious/Command/Generate/App.pm
 lib/Mojolicious/Command/Generate/Gitignore.pm
 lib/Mojolicious/Command/Generate/LiteApp.pm
 lib/Mojolicious/Command/Generate/Makefile.pm
+lib/Mojolicious/Command/Get.pm
 lib/Mojolicious/Command/Inflate.pm
+lib/Mojolicious/Command/Psgi.pm
 lib/Mojolicious/Command/Routes.pm
+lib/Mojolicious/Command/Test.pm
+lib/Mojolicious/Command/Version.pm
 lib/Mojolicious/Commands.pm
 lib/Mojolicious/Controller.pm
 lib/Mojolicious/Guides.pod
@@ -85,17 +84,13 @@ lib/Mojolicious/Plugin/PoweredBy.pm
 lib/Mojolicious/Plugin/RequestTimer.pm
 lib/Mojolicious/Plugin/TagHelpers.pm
 lib/Mojolicious/Plugins.pm
-lib/MojoX/Controller.pm
-lib/MojoX/Dispatcher/Routes.pm
-lib/MojoX/Dispatcher/Routes/Controller.pm
-lib/MojoX/Dispatcher/Static.pm
-lib/MojoX/Renderer.pm
-lib/MojoX/Routes.pm
-lib/MojoX/Routes/Match.pm
-lib/MojoX/Routes/Pattern.pm
-lib/MojoX/Session/Cookie.pm
-lib/MojoX/Session/Cookie/Controller.pm
-lib/MojoX/Types.pm
+lib/Mojolicious/Renderer.pm
+lib/Mojolicious/Routes.pm
+lib/Mojolicious/Routes/Match.pm
+lib/Mojolicious/Routes/Pattern.pm
+lib/Mojolicious/Session.pm
+lib/Mojolicious/Static.pm
+lib/Mojolicious/Types.pm
 lib/ojo.pm
 lib/Test/Mojo.pm
 LICENSE
@@ -122,6 +117,8 @@ t/mojo/fastcgi.t
 t/mojo/headers.t
 t/mojo/home.t
 t/mojo/ioloop.t
+t/mojo/ioloop_online.t
+t/mojo/ioloop_tls.t
 t/mojo/json.t
 t/mojo/lib/LoaderException.pm
 t/mojo/lib/LoaderException2.pm
@@ -139,6 +136,7 @@ t/mojo/template.t
 t/mojo/url.t
 t/mojolicious/app.t
 t/mojolicious/charset_lite_app.t
+t/mojolicious/dispatch.t
 t/mojolicious/dispatcher_lite_app.t
 t/mojolicious/embedded_lite_app.t
 t/mojolicious/external_lite_app.json
@@ -162,11 +160,14 @@ t/mojolicious/lib/PluginWithTemplate.pm
 t/mojolicious/lib/SingleFileTestApp.pm
 t/mojolicious/lite_app.t
 t/mojolicious/longpolling_lite_app.t
+t/mojolicious/pattern.t
 t/mojolicious/pod_renderer_lite_app.t
 t/mojolicious/production_app.t
 t/mojolicious/public/hello.txt
 t/mojolicious/public/hello2.txt
 t/mojolicious/public_dev/hello.txt
+t/mojolicious/renderer.t
+t/mojolicious/routes.t
 t/mojolicious/secret.txt
 t/mojolicious/templates/23.html.epl
 t/mojolicious/templates/encoding.koi8-r.ep
@@ -186,10 +187,6 @@ t/mojolicious/upload_lite_app.t
 t/mojolicious/websocket_lite_app.t
 t/mojolicious/websocket_proxy_lite_app.t
 t/mojolicious/websocket_tls_proxy_lite_app.t
-t/mojox/dispatcher.t
-t/mojox/pattern.t
-t/mojox/renderer.t
-t/mojox/routes.t
 t/pod.t
 t/pod_coverage.t
 META.yml                                 Module meta-data (added by MakeMaker)
@@ -1,6 +1,6 @@
 --- #YAML:1.0
 name:               Mojolicious
-version:            0.999936
+version:            0.999941
 abstract:           The Web In A Box!
 author:
     - Sebastian Riedel <sri@cpan.org>
@@ -29,6 +29,7 @@ requires:
     IO::File:             0
     IO::Poll:             0
     IO::Socket:           0
+    List::Util:           0
     Locale::Maketext:     0
     MIME::Base64:         0
     MIME::QuotedPrint:    0
@@ -64,6 +64,7 @@ WriteMakefile(
         'IO::File'              => 0,
         'IO::Poll'              => 0,
         'IO::Socket'            => 0,
+        'List::Util'            => 0,
         'Locale::Maketext'      => 0,
         'MIME::Base64'          => 0,
         'MIME::QuotedPrint'     => 0,
@@ -34,8 +34,8 @@ magic and no requirements besides Perl 5.8.7.
 
 =item *
 
-Full stack HTTP 1.1 and WebSocket client/server implementation with IPv6,
-TLS, Bonjour, IDNA, Comet (long polling), chunking and multipart support.
+Full stack HTTP 1.1 and WebSocket client/server implementation with TLS,
+Bonjour, IDNA, Comet (long polling), chunking and multipart support.
 
 =item *
 
@@ -98,9 +98,6 @@ Web development for humans, making hard things possible and everything fun.
         The time is <%= $hour %>:<%= $minute %>:<%= $second %>.
     <% end %>
 
-For more user friendly documentation see L<Mojolicious::Guides> and
-L<Mojolicious::Lite>.
-
 =head2 Have Some Cake
 
 Loosely coupled building blocks, use what you like and just ignore the rest.
@@ -33,7 +33,7 @@ $loop->listen(
         $buffer->{$id} .= $chunk;
 
         # Check if we got start line and headers (no body support)
-        if (index $buffer->{$id}, "\x0d\x0a\x0d\x0a") {
+        if (index($buffer->{$id}, "\x0d\x0a\x0d\x0a") >= 0) {
 
             # Clean buffer
             delete $buffer->{$id};
@@ -76,9 +76,6 @@ sub add_chunk {
     return $self;
 }
 
-# Your guilty consciences may make you vote Democratic, but secretly you all
-# yearn for a Republican president to lower taxes, brutalize criminals, and
-# rule you like a king!
 sub contains {
     my ($self, $bytestream) = @_;
     my ($buffer, $window);
@@ -82,6 +82,7 @@ sub attr {
         $code .= '};';
 
         # We compile custom attribute code for speed
+        no warnings 'redefine';
         *{"${class}::$attr"} = eval $code;
 
         # This should never happen (hopefully)
@@ -188,7 +188,7 @@ sub build_form_tx {
                 # Filename
                 $filename = delete $f->{filename} || $name;
                 encode $encoding, $filename if $encoding;
-                url_escape $filename, $Mojo::URL::PARAM;
+                url_escape $filename, $Mojo::URL::UNRESERVED;
 
                 # Asset
                 $part->asset(delete $f->{file});
@@ -213,7 +213,7 @@ sub build_form_tx {
 
             # Content-Disposition
             encode $encoding, $name if $encoding;
-            url_escape $name, $Mojo::URL::PARAM;
+            url_escape $name, $Mojo::URL::UNRESERVED;
             my $disposition = qq/form-data; name="$name"/;
             $disposition .= qq/; filename="$filename"/ if $filename;
             $h->content_disposition($disposition);
@@ -740,7 +740,7 @@ sub _drop {
         # Don't keep CONNECTed connections alive
         my $method = $tx->req->method || '';
         my $code   = $tx->res->code   || '';
-        unless ($method eq 'CONNECT' && $code eq '200') {
+        unless ($method =~ /^connect$/i && $code eq '200') {
 
             # Keep connection alive
             $self->_cache(join(':', $self->_tx_info($tx)), $id);
@@ -814,6 +814,10 @@ sub _handle {
         # Idle connection
         return unless $old;
 
+        # Interrupted
+        $old->res->error('Interrupted, maybe a timeout?')
+          unless $old->is_done;
+
         # Extract cookies
         if (my $jar = $self->cookie_jar) { $jar->extract($old) }
 
@@ -1121,8 +1125,8 @@ Mojo::Client - Async IO HTTP 1.1 And WebSocket Client
     my $client = Mojo::Client->new;
 
     # Grab the latest Mojolicious release :)
-    my $latest = 'http://mojolicious.org/Mojolicious-latest.tar.gz';
-    print $client->get($latest)->res->body;
+    my $latest = 'http://latest.mojolicio.us';
+    print $client->max_redirects(3)->get($latest)->res->body;
 
     # Quick JSON request
     my $trends = 'http://search.twitter.com/trends.json';
@@ -1172,10 +1176,10 @@ Mojo::Client - Async IO HTTP 1.1 And WebSocket Client
 =head1 DESCRIPTION
 
 L<Mojo::Client> is a full featured async io HTTP 1.1 and WebSocket client
-with C<IPv6>, C<TLS>, C<epoll> and C<kqueue> support.
+with C<TLS>, C<epoll> and C<kqueue> support.
 
-Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::IP> and
-L<IO::Socket::SSL> are supported transparently and used if installed.
+Optional modules L<IO::KQueue>, L<IO::Epoll> and L<IO::Socket::SSL> are
+supported transparently and used if installed.
 
 =head1 ATTRIBUTES
 
@@ -1,89 +0,0 @@
-package Mojo::Command::Cgi;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Server::CGI;
-
-use Getopt::Long 'GetOptions';
-
-__PACKAGE__->attr(description => <<'EOF');
-Start application with CGI.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 cgi [OPTIONS]
-
-These options are available:
-  --nph   Enable non-parsed-header mode.
-EOF
-
-# Hi, Super Nintendo Chalmers!
-sub run {
-    my $self = shift;
-    my $cgi  = Mojo::Server::CGI->new;
-
-    # Options
-    local @ARGV = @_ if @_;
-    GetOptions(nph => sub { $cgi->nph(1) });
-
-    # Run
-    $cgi->run;
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Cgi - CGI Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::CGI;
-
-    my $cgi = Mojo::Command::CGI->new;
-    $cgi->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Cgi> is a command interface to L<Mojo::Server::CGI>.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Cgi> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $cgi->description;
-    $cgi            = $cgi->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $cgi->usage;
-    $cgi      = $cgi->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Cgi> inherits all methods from L<Mojo::Command> and implements
-the following new ones.
-
-=head2 C<run>
-
-    $cgi = $cgi->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,117 +0,0 @@
-package Mojo::Command::Daemon;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Server::Daemon;
-
-use Getopt::Long 'GetOptions';
-
-__PACKAGE__->attr(description => <<'EOF');
-Start application with HTTP 1.1 and WebSocket server.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 daemon [OPTIONS]
-
-These options are available:
-  --clients <number>      Set maximum number of concurrent clients, defaults
-                          to 1000.
-  --group <name>          Set group name for process.
-  --keepalive <seconds>   Set keep-alive timeout, defaults to 15.
-  --listen <locations>    Set a comma separated list of locations you want to
-                          listen on, defaults to http://*:3000.
-  --queue <size>          Set listen queue size, defaults to SOMAXCONN.
-  --reload                Automatically reload application when the source
-                          code changes.
-  --requests <number>     Set maximum number of requests per keep-alive
-                          connection, defaults to 100.
-  --reverseproxy          Activate reverse proxy support, defaults to the
-                          value of MOJO_REVERSE_PROXY.
-  --user <name>           Set user name for process.
-  --websocket <seconds>   Set WebSocket timeout, defaults to 300.
-EOF
-
-
-# This is the worst thing you've ever done.
-# You say that so often that it lost its meaning.
-sub run {
-    my $self   = shift;
-    my $daemon = Mojo::Server::Daemon->new;
-
-    # Options
-    local @ARGV = @_ if @_;
-    GetOptions(
-        'clients=i'    => sub { $daemon->max_clients($_[1]) },
-        'group=s'      => sub { $daemon->group($_[1]) },
-        'keepalive=i'  => sub { $daemon->keep_alive_timeout($_[1]) },
-        'listen=s'     => sub { $daemon->listen($_[1]) },
-        'queue=i'      => sub { $daemon->listen_queue_size($_[1]) },
-        reload         => sub { $ENV{MOJO_RELOAD} = 1 },
-        'requests=i'   => sub { $daemon->max_requests($_[1]) },
-        'reverseproxy' => sub { $ENV{MOJO_REVERSE_PROXY} = 1 },
-        'user=s'       => sub { $daemon->user($_[1]) },
-        'websocket=i'  => sub { $daemon->websocket_timeout($_[1]) }
-    );
-
-    # Run
-    $daemon->run;
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Daemon - Daemon Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Daemon;
-
-    my $daemon = Mojo::Command::Daemon->new;
-    $daemon->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Daemon> is a command interface to
-L<Mojo::Server::Daemon>.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Daemon> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $daemon->description;
-    $daemon         = $daemon->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $daemon->usage;
-    $daemon   = $daemon->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Daemon> inherits all methods from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<run>
-
-    $daemon = $daemon->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,80 +0,0 @@
-package Mojo::Command::Fastcgi;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Server::FastCGI;
-
-__PACKAGE__->attr(description => <<'EOF');
-Start application with FastCGI.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 fastcgi
-EOF
-
-# Oh boy! Sleep! That's when I'm a Viking!
-sub run {
-    my $self    = shift;
-    my $fastcgi = Mojo::Server::FastCGI->new;
-
-    # Run
-    $fastcgi->run;
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Fastcgi - FastCGI Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Fastcgi;
-
-    my $fastcgi = Mojo::Command::Fastcgi->new;
-    $fastcgi->run;
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Fastcgi> is a command interface to L<Mojo::Server::FastCGI>.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::FastCGI> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $fastcgi->description;
-    $fastcgi        = $fastcgi->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $fastcgi->usage;
-    $fastcgi  = $fastcgi->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Fastcgi> inherits all methods from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<run>
-
-    $fastcgi = $fastcgi->run;
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,123 +0,0 @@
-package Mojo::Command::Get;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Client;
-use Mojo::IOLoop;
-use Mojo::Transaction::HTTP;
-use Mojo::Util 'decode';
-
-use Getopt::Long 'GetOptions';
-
-__PACKAGE__->attr(description => <<'EOF');
-Get file from URL.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 get [OPTIONS] [URL]
-
-These options are available:
-  --verbose   Print response start line and headers to STDERR.
-EOF
-
-# I hope this has taught you kids a lesson: kids never learn.
-sub run {
-    my $self = shift;
-
-    # Options
-    local @ARGV = @_ if @_;
-    my $verbose = 0;
-    GetOptions('verbose' => sub { $verbose = 1 });
-
-    # URL
-    my $url = $ARGV[0];
-    die $self->usage unless $url;
-    decode 'UTF-8', $url;
-
-    # Client
-    my $client = Mojo::Client->new(ioloop => Mojo::IOLoop->singleton);
-
-    # Silence
-    $client->log->level('fatal');
-
-    # Application
-    $client->app($ENV{MOJO_APP} || 'Mojo::HelloWorld')
-      unless $url =~ /^\w+:\/\//;
-
-    # Transaction
-    my $tx = $client->build_tx(GET => $url);
-    $tx->res->body(
-        sub {
-            my ($res, $chunk) = @_;
-            print STDERR $tx->res->build_start_line if $verbose;
-            print STDERR $res->headers->to_string, "\n\n" if $verbose;
-            print $chunk;
-            $verbose = 0;
-        }
-    );
-
-    # Request
-    $client->start($tx);
-
-    # Error
-    my ($message, $code) = $tx->error;
-    print qq/Couldn't open page "$url". ($message)\n/ if $message && !$code;
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Get - Get Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Get;
-
-    my $get = Mojo::Command::Get->new;
-    $get->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Get> is a command interface to L<Mojo::Client>.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Get> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $get->description;
-    $get            = $get->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $get->usage;
-    $get      = $get->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Get> inherits all methods from L<Mojo::Command> and implements
-the following new ones.
-
-=head2 C<run>
-
-    $get = $get->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,84 +0,0 @@
-package Mojo::Command::Psgi;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Server::PSGI;
-
-# Don't let Krusty's death get you down, boy.
-# People die all the time, just like that.
-# Why, you could wake up dead tomorrow! Well, good night.
-__PACKAGE__->attr(description => <<'EOF');
-Start application with PSGI.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 psgi
-EOF
-
-# D’oh.
-sub run {
-    my $self = shift;
-    my $psgi = Mojo::Server::PSGI->new;
-
-    # Preload
-    $psgi->app;
-
-    # Return app callback
-    return sub { $psgi->run(@_) };
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Psgi - PSGI Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Psgi;
-
-    my $psgi = Mojo::Command::Psgi->new;
-    my $app = $psgi->run;
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Psgi> is a command interface to L<Mojo::Server::PSGI>.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Psgi> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $psgi->description;
-    $psgi           = $psgi->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $psgi->usage;
-    $psgi     = $psgi->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Psgi> inherits all methods from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<run>
-
-    my $app = $psgi->run;
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,122 +0,0 @@
-package Mojo::Command::Test;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Cwd;
-use FindBin;
-use File::Spec;
-use Test::Harness;
-
-# Okay folks, show's over. Nothing to see here, show's... Oh my god!
-# A horrible plane crash! Hey everybody, get a load of this flaming wreckage!
-# Come on, crowd around, crowd around!
-__PACKAGE__->attr(description => <<'EOF');
-Run unit tests.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 test [TESTS]
-EOF
-
-# My eyes! The goggles do nothing!
-sub run {
-    my ($self, @tests) = @_;
-
-    # Search tests
-    unless (@tests) {
-        my @base = File::Spec->splitdir(File::Spec->abs2rel($FindBin::Bin));
-
-        # Test directory in the same directory as "mojo" (t)
-        my $path = File::Spec->catdir(@base, 't');
-
-        # Test dirctory in the directory above "mojo" (../t)
-        $path = File::Spec->catdir(@base, '..', 't') unless -d $path;
-        unless (-d $path) {
-            print "Can't find test directory.\n";
-            return;
-        }
-
-        # List test files
-        my @dirs = ($path);
-        while (my $dir = shift @dirs) {
-            opendir(my $fh, $dir);
-            for my $file (readdir($fh)) {
-                next if $file eq '.';
-                next if $file eq '..';
-                my $fpath = File::Spec->catfile($dir, $file);
-                push @dirs, File::Spec->catdir($dir, $file) if -d $fpath;
-                push @tests,
-                  File::Spec->abs2rel(
-                    Cwd::realpath(
-                        File::Spec->catfile(File::Spec->splitdir($fpath))
-                    )
-                  ) if (-f $fpath) && ($fpath =~ /\.t$/);
-            }
-            closedir $fh;
-        }
-
-        $path = Cwd::realpath($path);
-        print "Running tests from '$path'.\n";
-    }
-
-    # Run tests
-    runtests(@tests);
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Test - Test Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Test;
-
-    my $test = Mojo::Command::Test->new;
-    $test->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Test> is a test script.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Test> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $test->description;
-    $test           = $test->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $test->usage;
-    $test     = $test->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Test> inherits all methods from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<run>
-
-    $test = $test->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,135 +0,0 @@
-package Mojo::Command::Version;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Client;
-use Mojo::IOLoop;
-use Mojo::Server::Daemon;
-use Mojolicious;
-
-__PACKAGE__->attr(description => <<'EOF');
-Show versions of installed modules.
-EOF
-__PACKAGE__->attr(usage => <<"EOF");
-usage: $0 version
-
-EOF
-
-# If at first you don't succeed, give up.
-sub run {
-    my $self = shift;
-
-    # Mojo
-    my $mojo     = $Mojolicious::VERSION;
-    my $codename = $Mojolicious::CODENAME;
-
-    # Latest version
-    my $latest = $mojo;
-    eval {
-        Mojo::Client->new->max_redirects(3)
-          ->get('search.cpan.org/dist/Mojolicious')->res->dom('.version')
-          ->each(sub { $latest = $_->text if $_->text =~ /^[\d\.]+$/ });
-    };
-
-    # Message
-    my $message = 'This version is up to date, have fun!';
-    $message = 'Thanks for testing a development release, you are awesome!'
-      if $latest < $mojo;
-    $message = "You might want to update your Mojolicious to $latest."
-      if $latest > $mojo;
-
-    # Epoll
-    my $epoll = Mojo::IOLoop::EPOLL() ? $IO::Epoll::VERSION : 'not installed';
-
-    # KQueue
-    my $kqueue =
-      Mojo::IOLoop::KQUEUE() ? $IO::KQueue::VERSION : 'not installed';
-
-    # IPv6
-    my $ipv6 =
-      Mojo::IOLoop::IPV6() ? $IO::Socket::IP::VERSION : 'not installed';
-
-    # TLS
-    my $tls =
-      Mojo::IOLoop::TLS() ? $IO::Socket::SSL::VERSION : 'not installed';
-
-    # Bonjour
-    my $bonjour =
-      eval 'Mojo::Server::Daemon::BONJOUR()'
-      ? $Net::Rendezvous::Publish::VERSION
-      : 'not installed';
-
-    print <<"EOF";
-CORE
-  Perl        ($], $^O)
-  Mojolicious ($mojo, $codename)
-
-OPTIONAL
-  IO::Epoll                ($epoll)
-  IO::KQueue               ($kqueue)
-  IO::Socket::IP           ($ipv6)
-  IO::Socket::SSL          ($tls)
-  Net::Rendezvous::Publish ($bonjour)
-
-$message
-EOF
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Command::Version - Version Command
-
-=head1 SYNOPSIS
-
-    use Mojo::Command::Version;
-
-    my $v = Mojo::Command::Version->new;
-    $v->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Command::Version> shows versions of installed modules.
-
-=head1 ATTRIBUTES
-
-L<Mojo::Command::Version> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<description>
-
-    my $description = $v->description;
-    $v              = $v->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 C<usage>
-
-    my $usage = $v->usage;
-    $v        = $v->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojo::Command::Version> inherits all methods from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<run>
-
-    $get = $v->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -12,12 +12,26 @@ require IO::File;
 
 use Carp 'croak';
 use Mojo::Template;
-use Mojo::Util qw/b64_decode decamelize/;
+use Mojo::Loader;
+use Mojo::Util qw/b64_decode camelize decamelize/;
 
+__PACKAGE__->attr(hint => <<"EOF");
+
+See '$0 help COMMAND' for more information on a specific command.
+EOF
 __PACKAGE__->attr(description => 'No description.');
-__PACKAGE__->attr(quiet       => 0);
-__PACKAGE__->attr(renderer    => sub { Mojo::Template->new });
-__PACKAGE__->attr(usage       => "usage: $0\n");
+__PACKAGE__->attr(message     => <<"EOF");
+usage: $0 COMMAND [OPTIONS]
+
+Tip: CGI, FastCGI and PSGI environments can be automatically detected very
+     often and work without commands.
+
+These commands are currently available:
+EOF
+__PACKAGE__->attr(namespaces => sub { ['Mojo::Command'] });
+__PACKAGE__->attr(quiet      => 0);
+__PACKAGE__->attr(renderer   => sub { Mojo::Template->new });
+__PACKAGE__->attr(usage      => "usage: $0\n");
 
 sub chmod_file {
     my ($self, $path, $mod) = @_;
@@ -84,6 +98,29 @@ sub create_rel_dir {
     $self->create_dir($path);
 }
 
+sub detect {
+    my ($self, $guess) = @_;
+
+    # Hypnotoad
+    return 'hypnotoad' if defined $ENV{HYPNOTOAD_APP};
+
+    # PSGI (Plack only for now)
+    return 'psgi' if defined $ENV{PLACK_ENV};
+
+    # CGI
+    return 'cgi'
+      if defined $ENV{PATH_INFO} || defined $ENV{GATEWAY_INTERFACE};
+
+    # No further detection if we have a guess
+    return $guess if $guess;
+
+    # FastCGI (detect absence of WINDIR for Windows and USER for UNIX)
+    return 'fastcgi' if !defined $ENV{WINDIR} && !defined $ENV{USER};
+
+    # Nothing
+    return;
+}
+
 sub get_all_data {
     my ($self, $class) = @_;
     $class ||= ref $self;
@@ -201,7 +238,125 @@ sub render_to_rel_file {
 }
 
 # My cat's breath smells like cat food.
-sub run { croak 'Method "run" not implemented by subclass' }
+sub run {
+    my ($self, $name, @args) = @_;
+
+    # Try to detect environment
+    $name = $self->detect($name) unless $ENV{MOJO_NO_DETECT};
+
+    # Run command
+    if ($name && $name =~ /^\w+$/ && ($name ne 'help' || $args[0])) {
+
+        # Help
+        my $help = $name eq 'help' ? 1 : 0;
+        $name = shift @args if $help;
+
+        # Try all namespaces
+        my $module;
+        for my $namespace (@{$self->namespaces}) {
+
+            # Generate module
+            my $camelized = $name;
+            camelize $camelized;
+            my $try = "$namespace\::$camelized";
+
+            # Load
+            if (my $e = Mojo::Loader->load($try)) {
+
+                # Module missing
+                next unless ref $e;
+
+                # Real error
+                die $e;
+            }
+
+            # Module is a command
+            next unless $try->can('new') && $try->can('run');
+
+            # Found
+            $module = $try;
+            last;
+        }
+
+        # Command missing
+        die qq/Command "$name" missing, maybe you need to install it?\n/
+          unless $module;
+
+        # Run
+        my $command = $module->new;
+        return $help ? $command->help : $command->run(@args);
+    }
+
+    # Test
+    return $self if $ENV{HARNESS_ACTIVE};
+
+    # Try all namespaces
+    my $commands = [];
+    my $seen     = {};
+    for my $namespace (@{$self->namespaces}) {
+
+        # Search
+        if (my $modules = Mojo::Loader->search($namespace)) {
+            for my $module (@$modules) {
+
+                # Load
+                if (my $e = Mojo::Loader->load($module)) { die $e }
+
+                # Seen
+                my $command = $module;
+                $command =~ s/^$namespace\:://;
+                push @$commands, [$command => $module]
+                  unless $seen->{$command};
+                $seen->{$command} = 1;
+            }
+        }
+    }
+
+    # Print overview
+    print $self->message;
+
+    # Make list
+    my $list   = [];
+    my $length = 0;
+    foreach my $command (@$commands) {
+
+        # Generate name
+        my $name = $command->[0];
+        decamelize $name;
+
+        # Add to list
+        my $l = length $name;
+        $length = $l if $l > $length;
+        push @$list, [$name, $command->[1]->new->description];
+    }
+
+    # Print list
+    foreach my $command (@$list) {
+        my $name        = $command->[0];
+        my $description = $command->[1];
+        my $padding     = ' ' x ($length - length $name);
+        print "  $name$padding   $description";
+    }
+
+    # Hint
+    print $self->hint;
+
+    return $self;
+}
+
+sub start {
+    my $self = shift;
+
+    # Don't run commands if we are reloading
+    return $self if $ENV{MOJO_COMMANDS_DONE};
+    $ENV{MOJO_COMMANDS_DONE} ||= 1;
+
+    # Arguments
+    my @args = @_ ? @_ : @ARGV;
+
+    # Run
+    return ref $self ? $self->run(@args) : $self->new->run(@args);
+}
 
 sub write_file {
     my ($self, $path, $data) = @_;
@@ -278,7 +433,8 @@ Mojo::Command - Command Base Class
 =head1 DESCRIPTION
 
 L<Mojo::Command> is an abstract base class for L<Mojo> commands.
-See L<Mojo::Commands> for a list of commands that are available by default.
+See L<Mojolicious::Commands> for a list of commands that are available by
+default.
 
 =head1 ATTRIBUTES
 
@@ -291,6 +447,27 @@ L<Mojo::Command> implements the following attributes.
 
 Short description of command, used for the command list.
 
+=head2 C<hint>
+
+    my $hint  = $commands->hint;
+    $commands = $commands->hint('Foo!');
+
+Short hint shown after listing available commands.
+
+=head2 C<message>
+
+    my $message = $commands->message;
+    $commands   = $commands->message('Hello World!');
+
+Short usage message shown before listing available commands.
+
+=head2 C<namespaces>
+
+    my $namespaces = $commands->namespaces;
+    $commands      = $commands->namespaces(['Mojolicious::Commands']);
+
+Namespaces to search for available commands, defaults to L<Mojo::Command>.
+
 =head2 C<quiet>
 
     my $quiet = $command->quiet;
@@ -350,6 +527,13 @@ Portably create a directory.
 
 Portably create a relative directory.
 
+=head2 C<detect>
+
+    my $env = $commands->detect;
+    my $env = $commands->detect($guess);
+
+Try to detect environment.
+
 =head2 C<get_all_data>
 
     my $all = $command->get_all_data;
@@ -403,9 +587,17 @@ relative file.
 
 =head2 C<run>
 
-    $command = $command->run(@ARGV);
+    $commands->run;
+    $commands->run(@ARGV);
+
+Load and run commands.
+
+=head2 C<start>
+
+    Mojo::Command->start;
+    Mojo::Command->start(@ARGV);
 
-Run command.
+Start the command line interface.
 
 =head2 C<write_file>
 
@@ -1,309 +0,0 @@
-package Mojo::Commands;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Command';
-
-use Mojo::Loader;
-use Mojo::Util qw/camelize decamelize/;
-
-__PACKAGE__->attr(hint => <<"EOF");
-
-See '$0 help COMMAND' for more information on a specific command.
-EOF
-__PACKAGE__->attr(message => <<"EOF");
-usage: $0 COMMAND [OPTIONS]
-
-Tip: CGI, FastCGI and PSGI environments can be automatically detected very
-     often and work without commands.
-
-These commands are currently available:
-EOF
-__PACKAGE__->attr(namespaces => sub { ['Mojo::Command'] });
-
-# Aren't we forgetting the true meaning of Christmas?
-# You know, the birth of Santa.
-sub detect {
-    my ($self, $guess) = @_;
-
-    # Hypnotoad
-    return 'hypnotoad' if defined $ENV{HYPNOTOAD_APP};
-
-    # PSGI (Plack only for now)
-    return 'psgi' if defined $ENV{PLACK_ENV};
-
-    # CGI
-    return 'cgi'
-      if defined $ENV{PATH_INFO} || defined $ENV{GATEWAY_INTERFACE};
-
-    # No further detection if we have a guess
-    return $guess if $guess;
-
-    # FastCGI (detect absence of WINDIR for Windows and USER for UNIX)
-    return 'fastcgi' if !defined $ENV{WINDIR} && !defined $ENV{USER};
-
-    # Nothing
-    return;
-}
-
-sub run {
-    my ($self, $name, @args) = @_;
-
-    # Try to detect environment
-    $name = $self->detect($name) unless $ENV{MOJO_NO_DETECT};
-
-    # Run command
-    if ($name && $name =~ /^\w+$/ && ($name ne 'help' || $args[0])) {
-
-        # Help
-        my $help = $name eq 'help' ? 1 : 0;
-        $name = shift @args if $help;
-
-        # Try all namespaces
-        my $module;
-        for my $namespace (@{$self->namespaces}) {
-
-            # Generate module
-            my $camelized = $name;
-            camelize $camelized;
-            my $try = "$namespace\::$camelized";
-
-            # Load
-            if (my $e = Mojo::Loader->load($try)) {
-
-                # Module missing
-                next unless ref $e;
-
-                # Real error
-                die $e;
-            }
-
-            # Module is a command
-            next unless $try->can('new') && $try->can('run');
-
-            # Found
-            $module = $try;
-            last;
-        }
-
-        # Command missing
-        die qq/Command "$name" missing, maybe you need to install it?\n/
-          unless $module;
-
-        # Run
-        my $command = $module->new;
-        return $help ? $command->help : $command->run(@args);
-    }
-
-    # Test
-    return $self if $ENV{HARNESS_ACTIVE};
-
-    # Try all namespaces
-    my $commands = [];
-    my $seen     = {};
-    for my $namespace (@{$self->namespaces}) {
-
-        # Search
-        if (my $modules = Mojo::Loader->search($namespace)) {
-            for my $module (@$modules) {
-
-                # Load
-                if (my $e = Mojo::Loader->load($module)) { die $e }
-
-                # Seen
-                my $command = $module;
-                $command =~ s/^$namespace\:://;
-                push @$commands, [$command => $module]
-                  unless $seen->{$command};
-                $seen->{$command} = 1;
-            }
-        }
-    }
-
-    # Print overview
-    print $self->message;
-
-    # Make list
-    my $list   = [];
-    my $length = 0;
-    foreach my $command (@$commands) {
-
-        # Generate name
-        my $name = $command->[0];
-        decamelize $name;
-
-        # Add to list
-        my $l = length $name;
-        $length = $l if $l > $length;
-        push @$list, [$name, $command->[1]->new->description];
-    }
-
-    # Print list
-    foreach my $command (@$list) {
-        my $name        = $command->[0];
-        my $description = $command->[1];
-        my $padding     = ' ' x ($length - length $name);
-        print "  $name$padding   $description";
-    }
-
-    # Hint
-    print $self->hint;
-
-    return $self;
-}
-
-sub start {
-    my $self = shift;
-
-    # Don't run commands if we are reloading
-    return $self if $ENV{MOJO_COMMANDS_DONE};
-    $ENV{MOJO_COMMANDS_DONE} ||= 1;
-
-    # Arguments
-    my @args = @_ ? @_ : @ARGV;
-
-    # Run
-    return ref $self ? $self->run(@args) : $self->new->run(@args);
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Mojo::Commands - Commands
-
-=head1 SYNOPSIS
-
-    use Mojo::Commands;
-
-    # Command line interface
-    my $commands = Mojo::Commands->new;
-    $commands->run(@ARGV);
-
-=head1 DESCRIPTION
-
-L<Mojo::Commands> is the interactive command line interface to the L<Mojo>
-framework.
-It will automatically detect available commands in the L<Mojo::Command>
-namespace.
-Commands are implemented by subclassing L<Mojo::Command>.
-
-These commands are available by default.
-
-=over 4
-
-=item C<help>
-
-    mojo
-    mojo help
-
-List available commands with short descriptions.
-
-    mojo help <command>
-
-List available options for the command with short descriptions.
-
-=item C<cgi>
-
-    mojo cgi
-    script/myapp cgi
-
-Start application with CGI backend.
-
-=item C<daemon>
-
-    mojo cgi
-    script/myapp daemon
-
-Start application with standalone HTTP 1.1 server backend.
-
-=item C<fastcgi>
-
-    mojo fastcgi
-    script/myapp fastcgi
-
-Start application with FastCGI backend.
-
-=item C<get>
-
-   mojo get http://mojolicious.org
-   script/myapp get /foo
-
-Perform GET request to remote host or local application.
-
-=item C<test>
-
-   mojo test
-   script/myapp test
-   script/myapp test t/foo.t
-
-Runs application tests from the C<t> directory.
-
-=item C<version>
-
-    mojo version
-
-List version information for installed core and optional modules, very useful
-for debugging.
-
-=back
-
-=head1 ATTRIBUTES
-
-L<Mojo::Commands> inherits all attributes from L<Mojo::Command> and
-implements the following new ones.
-
-=head2 C<hint>
-
-    my $hint  = $commands->hint;
-    $commands = $commands->hint('Foo!');
-
-Short hint shown after listing available commands.
-
-=head2 C<message>
-
-    my $message  = $commands->message;
-    $commands    = $commands->message('Hello World!');
-
-Short usage message shown before listing available commands.
-
-=head2 C<namespaces>
-
-    my $namespaces = $commands->namespaces;
-    $commands      = $commands->namespaces(['Mojo::Command']);
-
-Namespaces to search for available commands, defaults to L<Mojo::Command>.
-
-=head1 METHODS
-
-L<Mojo::Commands> inherits all methods from L<Mojo::Command> and implements
-the following new ones.
-
-=head2 C<detect>
-
-    my $env = $commands->detect;
-    my $env = $commands->detect($guess);
-
-Try to detect environment.
-
-=head2 C<run>
-
-    $commands->run;
-    $commands->run(@ARGV);
-
-Load and run commands.
-
-=head2 C<start>
-
-    Mojo::Commands->start;
-    Mojo::Commands->start(@ARGV);
-
-Start the command line interface.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -68,8 +68,8 @@ sub build_headers {
     return $headers;
 }
 
-sub finish { shift->{_eof} = 1 }
-
+# Aren't we forgetting the true meaning of Christmas?
+# You know, the birth of Santa.
 sub generate_body_chunk {
     my ($self, $offset) = @_;
 
@@ -300,7 +300,7 @@ sub write_chunk {
     $self->write(defined $chunk ? $self->_build_chunk($chunk) : $chunk, $cb);
 
     # Finish
-    $self->finish if defined $chunk && $chunk eq '';
+    $self->{_eof} = 1 if defined $chunk && $chunk eq '';
 }
 
 sub _build_chunk {
@@ -504,13 +504,6 @@ Render whole body.
 
 Render all headers.
 
-=head2 C<finish>
-
-    $content->finish;
-
-Finish dynamic content generation.
-Note that this method is EXPERIMENTAL and might change without warning!
-
 =head2 C<generate_body_chunk>
 
     my $chunk = $content->generate_body_chunk(0);
@@ -1394,9 +1394,9 @@ Children of element.
 
 =head2 C<find>
 
-    my $results = $dom->find('html title');
+    my $collection = $dom->find('html title');
 
-Find elements with CSS3 selectors.
+Find elements with CSS3 selectors and return a collection.
 
     print $dom->find('div')->[23]->text;
     $dom->find('div')->each(sub { print shift->text });
@@ -11,6 +11,7 @@ use File::Spec;
 use IO::File;
 use IO::Poll qw/POLLERR POLLHUP POLLIN POLLOUT/;
 use IO::Socket;
+use List::Util 'first';
 use Mojo::URL;
 use Scalar::Util 'weaken';
 use Socket qw/IPPROTO_TCP TCP_NODELAY/;
@@ -19,8 +20,12 @@ use Time::HiRes 'time';
 # Debug
 use constant DEBUG => $ENV{MOJO_IOLOOP_DEBUG} || 0;
 
+# Perl 5.12 required for "inet_pton"
+use constant PTON => eval 'use 5.12.0; 1';
+use constant PTON_AF_INET6 => PTON ? Socket::AF_INET6() : 0;
+
 # Epoll support requires IO::Epoll
-use constant EPOLL => ($ENV{MOJO_POLL} || $ENV{MOJO_KQUEUE})
+use constant EPOLL => $ENV{MOJO_POLL}
   ? 0
   : eval 'use IO::Epoll 0.02 (); 1';
 use constant EPOLL_POLLERR => EPOLL ? IO::Epoll::POLLERR() : 0;
@@ -28,13 +33,8 @@ use constant EPOLL_POLLHUP => EPOLL ? IO::Epoll::POLLHUP() : 0;
 use constant EPOLL_POLLIN  => EPOLL ? IO::Epoll::POLLIN()  : 0;
 use constant EPOLL_POLLOUT => EPOLL ? IO::Epoll::POLLOUT() : 0;
 
-# IPv6 support requires IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6}
-  ? 0
-  : eval 'use IO::Socket::IP 0.04 (); 1';
-
 # KQueue support requires IO::KQueue
-use constant KQUEUE => ($ENV{MOJO_POLL} || $ENV{MOJO_EPOLL})
+use constant KQUEUE => $ENV{MOJO_POLL}
   ? 0
   : eval 'use IO::KQueue 0.34 (); 1';
 use constant KQUEUE_ADD    => KQUEUE ? IO::KQueue::EV_ADD()       : 0;
@@ -44,9 +44,9 @@ use constant KQUEUE_READ   => KQUEUE ? IO::KQueue::EVFILT_READ()  : 0;
 use constant KQUEUE_WRITE  => KQUEUE ? IO::KQueue::EVFILT_WRITE() : 0;
 
 # TLS support requires IO::Socket::SSL
-use constant TLS => $ENV{MOJO_NO_TLS} ? 0
-  : IPV6 ? eval 'use IO::Socket::SSL 1.33 (); 1'
-  :        eval 'use IO::Socket::SSL 1.33 "inet4"; 1';
+use constant TLS => $ENV{MOJO_NO_TLS}
+  ? 0
+  : eval 'use IO::Socket::SSL 1.34 "inet4"; 1';
 use constant TLS_READ  => TLS ? IO::Socket::SSL::SSL_WANT_READ()  : 0;
 use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
 
@@ -120,9 +120,13 @@ if (-r '/etc/resolv.conf') {
 
 # DNS record types
 my $DNS_TYPES = {
-    A    => 0x0001,
-    AAAA => 0x001c,
-    TXT  => 0x0010
+    A     => 0x0001,
+    AAAA  => 0x001c,
+    CNAME => 0x0005,
+    MX    => 0x000f,
+    NS    => 0x0002,
+    PTR   => 0x000c,
+    TXT   => 0x0010
 };
 
 # "localhost"
@@ -137,7 +141,7 @@ __PACKAGE__->attr(
         sub {1}
     }
 );
-__PACKAGE__->attr(timeout => '0.25');
+__PACKAGE__->attr(timeout => '0.025');
 
 # Singleton
 our $LOOP;
@@ -193,7 +197,8 @@ sub connect {
         on_connect => $args->{on_connect}
           || $args->{connect_cb}
           || $args->{cb},
-        connecting => 1
+        connecting => 1,
+        tls        => $args->{tls}
     };
     (my $id) = "$c" =~ /0x([\da-f]+)/;
     $self->{_cs}->{$id} = $c;
@@ -276,7 +281,7 @@ sub listen {
     my $args = ref $_[0] ? $_[0] : {@_};
 
     # TLS check
-    croak "IO::Socket::SSL 1.33 required for TLS support"
+    croak "IO::Socket::SSL 1.34 required for TLS support"
       if $args->{tls} && !TLS;
 
     # Options
@@ -329,14 +334,16 @@ sub listen {
     else {
 
         # Socket options
-        $options{LocalAddr} = $args->{address} || (IPV6 ? '::' : '0.0.0.0');
+        $options{LocalAddr} = $args->{address} || '0.0.0.0';
         $options{LocalPort} = $port;
         $options{Proto}     = 'tcp';
         $options{ReuseAddr} = 1;
 
         # Create socket
-        my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
-        $socket = defined $fd ? $class->new : $class->new(%options)
+        $socket =
+          defined $fd
+          ? IO::Socket::INET->new
+          : IO::Socket::INET->new(%options)
           or croak "Can't create listen socket: $!";
     }
 
@@ -397,7 +404,8 @@ sub lookup {
             my ($self, $results) = @_;
 
             # Success
-            return $self->$cb($results->[0]) if $results->[0];
+            my $result = first { $_->[0] eq 'A' } @$results;
+            return $self->$cb($result->[1]) if $result;
 
             # IPv6
             $self->resolve(
@@ -406,7 +414,8 @@ sub lookup {
                     my ($self, $results) = @_;
 
                     # Success
-                    return $self->$cb($results->[0]) if $results->[0];
+                    my $result = first { $_->[0] eq 'AAA' } @$results;
+                    return $self->$cb($result->[1]) if $result;
 
                     # Pass through
                     $self->$cb();
@@ -552,8 +561,10 @@ sub resolve {
     my ($self, $name, $type, $cb) = @_;
 
     # Regex
-    my $ipv4 = $Mojo::URL::IPV4_RE;
-    my $ipv6 = $Mojo::URL::IPV6_RE;
+    my $ipv4;
+    $ipv4 = 1 if $name =~ $Mojo::URL::IPV4_RE;
+    my $ipv6;
+    $ipv6 = 1 if PTON && $name =~ $Mojo::URL::IPV6_RE;
 
     # Type
     my $t = $DNS_TYPES->{$type};
@@ -562,7 +573,7 @@ sub resolve {
     my $server = $self->dns_server;
 
     # No lookup required or record type not supported
-    unless ($server && $t && $name !~ $ipv4 && $name !~ $ipv6) {
+    if (!$server || !$t || ($t ne $DNS_TYPES->{PTR} && ($ipv4 || $ipv6))) {
         $self->timer(0 => sub { $self->$cb([]) });
         return $self;
     }
@@ -587,8 +598,24 @@ sub resolve {
             # Header (one question with recursion)
             my $req = pack 'nnnnnn', $tx, 0x0100, 1, 0, 0, 0;
 
+            # Parts
+            my @parts = split /\./, $name;
+
+            # Reverse
+            if ($t eq $DNS_TYPES->{PTR}) {
+
+                # IPv4
+                if ($ipv4) { @parts = reverse 'arpa', 'in-addr', @parts }
+
+                # IPv6
+                elsif ($ipv6) {
+                    @parts = reverse 'arpa', 'ip6', split //, unpack 'H32',
+                      Socket::inet_pton(PTON_AF_INET6, $name);
+                }
+            }
+
             # Query (Internet)
-            for my $part (split /\./, $name) {
+            for my $part (@parts) {
                 $req .= pack 'C/a', $part if defined $part;
             }
             $req .= pack 'Cnn', 0, $t, 0x0001;
@@ -615,6 +642,9 @@ sub resolve {
             # Packet
             my @packet = unpack 'nnnnnna*', $chunk;
 
+            # Debug
+            warn "ANSWERS $packet[3] ($server)\n" if DEBUG;
+
             # Wrong response
             return $self->$cb([]) unless $packet[0] eq $tx;
 
@@ -624,37 +654,28 @@ sub resolve {
             # Questions
             for (1 .. $packet[2]) {
                 my $n;
-                do { ($n, $content) = unpack 'C/aa*', $content } while ($n);
+                do { ($n, $content) = unpack 'C/aa*', $content }
+                  while ($n ne '');
                 $content = (unpack 'nna*', $content)[2];
             }
 
             # Answers
             my @answers;
             for (1 .. $packet[3]) {
-                my ($t, $a, $answer);
-                ($t, $a, $content) = (unpack 'nnnNn/aa*', $content)[1, 4, 5];
 
-                # A
-                if ($t eq $DNS_TYPES->{A}) {
-                    $answer = join('.', unpack 'C4', $a);
-                }
-
-                # AAAA
-                elsif ($t eq $DNS_TYPES->{AAAA}) {
-                    $answer = sprintf '%x:%x:%x:%x:%x:%x:%x:%x',
-                      unpack('n*', $a);
-                }
+                # Parse
+                (my ($t, $a), $content) =
+                  (unpack 'nnnNn/aa*', $content)[1, 4, 5];
+                my @answer = _parse_answer($t, $a, $chunk, $content);
 
-                # TXT
-                elsif ($t eq $DNS_TYPES->{TXT}) {
-                    $answer = unpack '(C/a*)*', $a;
-                }
+                # No answer
+                next unless @answer;
 
-                next unless defined $answer;
-                push @answers, $answer;
+                # Answer
+                push @answers, \@answer;
 
                 # Debug
-                warn "ANSWER $answer\n" if DEBUG;
+                warn "ANSWER $answer[0] $answer[1]\n" if DEBUG;
             }
 
             # Done
@@ -670,9 +691,6 @@ sub resolve {
             # Debug
             warn "RESOLVE TIMEOUT ($server)\n" if DEBUG;
 
-            # Disable
-            $self->dns_server(undef);
-
             # Abort
             $self->drop($id);
             $self->$cb([]);
@@ -825,27 +843,19 @@ sub _accept {
     weaken $self;
 
     # Connection
-    my $c = {
-        accepting => 1,
-        buffer    => '',
-    };
+    my $c = {buffer => ''};
     (my $id) = "$c" =~ /0x([\da-f]+)/;
     $self->{_cs}->{$id} = $c;
 
     # TLS handshake
     my $tls = $l->{tls};
-    if ($tls) {
-        $tls->{SSL_error_trap} = sub { $self->_drop_immediately(shift) };
-        $socket = IO::Socket::SSL->start_SSL($socket, %$tls);
-    }
+    $socket = IO::Socket::SSL->start_SSL($socket, %$tls) if $tls;
     $c->{tls_accept} = 1 if $tls;
     $c->{socket}     = $socket;
     $r->{$socket}    = $id;
 
-    # Timeout
-    $c->{accept_timer} =
-      $self->timer($self->accept_timeout, =>
-          sub { shift->_error($id, 'Accept timeout.') });
+    # Non-blocking
+    $socket->blocking(0);
 
     # Disable Nagle's algorithm
     setsockopt($socket, IPPROTO_TCP, TCP_NODELAY, 1) unless $l->{file};
@@ -860,9 +870,15 @@ sub _accept {
         $self->$name($id => $cb) if $cb;
     }
 
+    # Add socket to poll
+    $self->_not_writing($id);
+
+    # Debug
+    warn "ACCEPTED $id\n" if DEBUG;
+
     # Accept callback
-    my $cb = $l->{on_accept};
-    $self->_run_event('accept', $cb, $id) if $cb;
+    my $cb = $c->{on_accept} = $l->{on_accept};
+    $self->_run_event('accept', $cb, $id) if $cb && !$l->{tls};
 
     # Remove listen sockets
     $listen = $self->{_listen} || {};
@@ -903,6 +919,7 @@ sub _connect {
 
     # Options
     my %options = (
+        Blocking => 0,
         PeerAddr => $args->{address},
         PeerPort => $args->{port} || ($args->{tls} ? 443 : 80),
         Proto    => $args->{proto},
@@ -911,9 +928,9 @@ sub _connect {
     );
 
     # Socket
-    my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
     return $self->_error($id, "Couldn't connect.")
-      unless my $socket = $args->{socket} || $class->new(%options);
+      unless my $socket = $args->{socket}
+          || IO::Socket::INET->new(%options);
     $c->{socket} = $socket;
     $self->{_reverse}->{$socket} = $id;
 
@@ -933,7 +950,7 @@ sub _connect {
           sub { shift->_error($id, 'Connect timeout.') });
 
     # Add socket to poll
-    $self->_not_writing($id);
+    $self->_writing($id);
 
     # Start TLS
     if ($args->{tls}) { $self->start_tls($id => $args) }
@@ -972,6 +989,9 @@ sub _drop_immediately {
     # Drop socket
     if (my $socket = $c->{socket}) {
 
+        # Debug
+        warn "DISCONNECTED $id\n" if DEBUG;
+
         # Remove file descriptor
         return unless my $fd = fileno $socket;
         delete $self->{_fds}->{$fd};
@@ -1080,26 +1100,74 @@ sub _not_writing {
     $c->{writing} = 0;
 }
 
-sub _prepare_accept {
-    my ($self, $id) = @_;
+# Answer helper for "resolve"
+sub _parse_answer {
+    my ($t, $a, $packet, $rest) = @_;
 
-    # Connection
-    my $c = $self->{_cs}->{$id};
+    # A
+    if ($t eq $DNS_TYPES->{A}) { return A => join('.', unpack 'C4', $a) }
 
-    # Connected
-    return unless $c->{socket}->connected;
+    # AAAA
+    elsif ($t eq $DNS_TYPES->{AAAA}) {
+        return AAAA => sprintf('%x:%x:%x:%x:%x:%x:%x:%x', unpack('n*', $a));
+    }
 
-    # Accepted
-    delete $c->{accepting};
+    # TXT
+    elsif ($t eq $DNS_TYPES->{TXT}) { return TXT => unpack('(C/a*)*', $a) }
 
-    # Remove timeout
-    $self->_drop_immediately(delete $c->{accept_timer});
+    # Offset
+    my $offset = length($packet) - length($rest) - length($a);
 
-    # Non-blocking
-    $c->{socket}->blocking(0);
+    # CNAME
+    my $type;
+    if ($t eq $DNS_TYPES->{CNAME}) { $type = 'CNAME' }
 
-    # Add socket to poll
-    $self->_not_writing($id);
+    # MX
+    elsif ($t eq $DNS_TYPES->{MX}) {
+        $type = 'MX';
+        $offset += 2;
+    }
+
+    # NS
+    elsif ($t eq $DNS_TYPES->{NS}) { $type = 'NS' }
+
+    # PTR
+    elsif ($t eq $DNS_TYPES->{PTR}) { $type = 'PTR' }
+
+    # Domain name
+    return $type => _parse_name($packet, $offset) if $type;
+
+    # Not supported
+    return;
+}
+
+# Domain name helper for "resolve"
+sub _parse_name {
+    my ($packet, $offset) = @_;
+
+    # Elements
+    my @elements;
+    for (1 .. 128) {
+
+        # Element length
+        my $length = ord substr $packet, $offset++, 1;
+
+        # Offset
+        if ($length >= 0xc0) {
+            $offset = (unpack 'n', substr $packet, ++$offset - 2, 2) & 0x3fff;
+        }
+
+        # Element
+        elsif ($length) {
+            push @elements, substr $packet, $offset, $length;
+            $offset += $length;
+        }
+
+        # Zero length element (the end)
+        else { return join '.', @elements }
+    }
+
+    return;
 }
 
 sub _prepare_cert {
@@ -1120,27 +1188,6 @@ sub _prepare_cert {
     return $self->{_cert} = $cert;
 }
 
-sub _prepare_connect {
-    my ($self, $id) = @_;
-
-    # Connection
-    my $c = $self->{_cs}->{$id};
-
-    # Not yet connected
-    return unless my $socket = $c->{socket};
-    if ($socket->can('connected')) { return unless $socket->connected }
-
-    # Connected
-    delete $c->{connecting};
-
-    # Remove timeout
-    $self->_drop_immediately(delete $c->{connect_timer});
-
-    # Connect callback
-    my $cb = $c->{on_connect};
-    $self->_run_event('connect', $cb, $id) if $cb;
-}
-
 sub _prepare_connections {
     my $self = shift;
 
@@ -1150,20 +1197,12 @@ sub _prepare_connections {
     # Prepare
     while (my ($id, $c) = each %$cs) {
 
-        # Accepting
-        $self->_prepare_accept($id) if $c->{accepting};
-
-        # Connecting
-        $self->_prepare_connect($id) if $c->{connecting};
-
         # Connection needs to be finished
-        if ($c->{finish}) {
+        if ($c->{finish} && !length $c->{buffer}) {
 
             # Buffer empty
-            unless (length $c->{buffer}) {
-                $self->_drop_immediately($id);
-                next;
-            }
+            $self->_drop_immediately($id);
+            next;
         }
 
         # Read only
@@ -1370,23 +1409,21 @@ sub _tls_accept {
     # Connection
     my $c = $self->{_cs}->{$id};
 
-    # Connected
+    # Accepted
     if ($c->{socket}->accept_SSL) {
-        delete $c->{tls_accept};
-        return;
-    }
 
-    # Error
-    my $error = $IO::Socket::SSL::SSL_ERROR;
+        # Cleanup
+        delete $c->{tls_accept};
 
-    # Reading
-    if ($error == TLS_READ) { $self->_not_writing($id) }
+        # Accept callback
+        my $cb = $c->{on_accept};
+        $self->_run_event('accept', $cb, $id) if $cb;
 
-    # Writing
-    elsif ($error == TLS_WRITE) { $self->_writing($id) }
+        return;
+    }
 
-    # Real error
-    else { $self->_error($id, $error) }
+    # Handle error
+    $self->_tls_error($id);
 }
 
 sub _tls_connect {
@@ -1397,10 +1434,24 @@ sub _tls_connect {
 
     # Connected
     if ($c->{socket}->connect_SSL) {
+
+        # Cleanup
         delete $c->{tls_connect};
+
+        # Connect callback
+        my $cb = $c->{on_connect};
+        $self->_run_event('connect', $cb, $id) if $cb;
+
         return;
     }
 
+    # Handle error
+    $self->_tls_error($id);
+}
+
+sub _tls_error {
+    my ($self, $id) = @_;
+
     # Error
     my $error = $IO::Socket::SSL::SSL_ERROR;
 
@@ -1426,13 +1477,25 @@ sub _write {
     # TLS connect
     return $self->_tls_connect($id) if $c->{tls_connect};
 
-    # Connect has just completed
-    return if $c->{connecting};
-
     # Socket
     return unless my $socket = $c->{socket};
     return unless $socket->connected;
 
+    # Connecting
+    if ($c->{connecting}) {
+
+        # Cleanup
+        delete $c->{connecting};
+        $self->_drop_immediately(delete $c->{connect_timer});
+
+        # Debug
+        warn "CONNECTED $id\n" if DEBUG;
+
+        # Connect callback
+        my $cb = $c->{on_connect};
+        $self->_run_event('connect', $cb, $id) if $cb && !$c->{tls};
+    }
+
     # Callback
     if ($c->{drain} && (my $event = delete $c->{drain})) {
         $self->_run_event('drain', $event, $id);
@@ -1506,7 +1569,7 @@ __END__
 
 =head1 NAME
 
-Mojo::IOLoop - Minimalistic Reactor For Non-Blocking TCP Clients And Servers
+Mojo::IOLoop - Minimalistic Reactor For Async TCP Clients And Servers
 
 =head1 SYNOPSIS
 
@@ -1561,11 +1624,11 @@ Mojo::IOLoop - Minimalistic Reactor For Non-Blocking TCP Clients And Servers
 =head1 DESCRIPTION
 
 L<Mojo::IOLoop> is a very minimalistic reactor that has been reduced to the
-absolute minimal feature set required to build solid and scalable
-non-blocking TCP clients and servers.
+absolute minimal feature set required to build solid and scalable async TCP
+clients and servers.
 
-Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::IP> and
-L<IO::Socket::SSL> are supported transparently and used if installed.
+Optional modules L<IO::KQueue>, L<IO::Epoll> and L<IO::Socket::SSL> are
+supported transparently and used if installed.
 
 A TLS certificate and key are also built right in to make writing test
 servers as easy as possible.
@@ -1671,7 +1734,7 @@ Note that exceptions in this callback are not captured.
     $loop       = $loop->timeout(5);
 
 Maximum time in seconds our loop waits for new events to happen, defaults to
-C<0.25>.
+C<0.025>.
 Note that a value of C<0> would make the loop non-blocking.
 
 =head1 METHODS
@@ -1699,10 +1762,8 @@ possible.
         tls     => 1
     });
 
-Open a TCP connection to a remote host, IPv6 will be used automatically if
-available.
-Note that IPv6 support depends on L<IO::Socket::IP> and TLS support on
-L<IO::Socket::SSL>.
+Open a TCP connection to a remote host.
+Note that TLS support depends on L<IO::Socket::SSL>.
 
 These options are currently available.
 
@@ -1788,9 +1849,8 @@ Check if loop is running.
         tls_key  => '/foo/server.key'
     );
 
-Create a new listen socket, IPv6 will be used automatically if available.
-Note that IPv6 support depends on L<IO::Socket::IP> and TLS support on
-L<IO::Socket::SSL>.
+Create a new listen socket.
+Note that TLS support depends on L<IO::Socket::SSL>.
 
 These options are currently available.
 
@@ -1934,7 +1994,8 @@ The remote port.
 
     $loop = $loop->resolve('mojolicio.us', 'A', sub {...});
 
-Resolve domain into C<A>, C<AAAA> or C<TXT> records.
+Resolve domain into C<A>, C<AAAA>, C<CNAME>, C<MX>, C<NS>, C<PTR> or C<TXT>
+records.
 Note that this method is EXPERIMENTAL and might change without warning!
 
 =head2 C<singleton>
@@ -28,10 +28,20 @@ sub load {
     # Shortcut
     return 1 unless $module;
 
+    # Forced reload
+    if ($ENV{MOJO_RELOAD}) {
+
+        # Unload
+        my $key = $module;
+        $key =~ s/\:\:/\//g;
+        $key .= '.pm';
+        _unload($key);
+    }
+
     # Already loaded
-    return if $module->can('new');
+    else { return if $module->can('new') }
 
-    # Try
+    # Load
     eval "require $module";
 
     # Catch
@@ -62,17 +72,10 @@ sub reload {
         if ($mtime > $STATS->{$file}) {
 
             # Debug
-            warn "\n$key -> $file modified, reloading!\n" if DEBUG;
+            warn "$key -> $file modified, reloading!\n" if DEBUG;
 
             # Unload
-            delete $INC{$key};
-            my @subs = grep { index($DB::sub{$_}, "$file:") == 0 }
-              keys %DB::sub;
-            for my $sub (@subs) {
-                eval { undef &$sub };
-                carp "Can't unload sub '$sub' in '$file': $@" if $@;
-                delete $DB::sub{$sub};
-            }
+            _unload($key);
 
             # Try
             eval { require $key };
@@ -125,6 +128,22 @@ sub search {
     return $modules;
 }
 
+sub _unload {
+    my $key = shift;
+
+    # Unload
+    my $file = $INC{$key};
+    delete $INC{$key};
+    return unless $file;
+    my @subs = grep { index($DB::sub{$_}, "$file:") == 0 }
+      keys %DB::sub;
+    for my $sub (@subs) {
+        eval { undef &$sub };
+        carp "Can't unload sub '$sub' in '$file': $@" if $@;
+        delete $DB::sub{$sub};
+    }
+}
+
 1;
 __END__
 
@@ -28,7 +28,6 @@ __PACKAGE__->attr(version => '1.1');
 # DEPRECATED in Comet!
 *finish_cb   = \&on_finish;
 *progress_cb = \&on_progress;
-*read_cb     = \&on_read;
 
 # I'll keep it short and sweet. Family. Religion. Friendship.
 # These are the three demons you must slay if you wish to succeed in
@@ -53,28 +52,33 @@ sub body {
     $self->content(Mojo::Content::Single->new)
       if $self->content->isa('Mojo::Content::MultiPart');
 
+    # Content
+    my $content = $self->content;
+
     # Get
     unless (@_) {
-        return $self->on_read
-          ? $self->on_read
+        return $content->on_read
+          ? $content->on_read
           : return $self->content->asset->slurp;
     }
 
     # New content
-    my $content = shift;
+    my $new = shift;
 
     # Cleanup
-    $self->on_read(undef);
-    $self->content->asset(Mojo::Asset::Memory->new);
+    $content->on_read(undef);
+    $content->asset(Mojo::Asset::Memory->new);
 
     # Shortcut
-    return $self unless defined $content;
+    return $self unless defined $new;
 
     # Callback
-    if (ref $content eq 'CODE') { $self->on_read($content) }
+    if (ref $new eq 'CODE') {
+        $content->on_read(sub { shift and $self->$new(@_) });
+    }
 
     # Set text content
-    elsif (length $content) { $self->content->asset->add_chunk($content) }
+    elsif (length $new) { $content->asset->add_chunk($new) }
 
     return $self;
 }
@@ -253,8 +257,6 @@ sub error {
     return $self;
 }
 
-sub finish { shift->content->finish(@_) }
-
 sub fix_headers {
     my $self = shift;
 
@@ -377,8 +379,6 @@ sub param {
 sub parse            { shift->_parse(0, @_) }
 sub parse_until_body { shift->_parse(1, @_) }
 
-sub on_read { shift->content->on_read(@_) }
-
 sub start_line_size { length shift->build_start_line }
 
 sub to_string {
@@ -696,20 +696,6 @@ Callback called after message building or parsing is finished.
 
 Progress callback.
 
-=head2 C<on_read>
-
-    my $cb   = $message->on_read;
-    $message = $message->on_read(sub {...});
-
-Content parser callback.
-
-    $message = $message->on_read(sub {
-        my ($self, $chunk) = @_;
-        print $chunk;
-    });
-
-Note that this attribute is EXPERIMENTAL and might change without warning!
-
 =head1 METHODS
 
 L<Mojo::Message> inherits all methods from L<Mojo::Base> and implements the
@@ -771,8 +757,8 @@ Access message cookies.
     my $dom        = $message->dom;
     my $collection = $message->dom('a[href]');
 
-Parses content into a L<Mojo::DOM> object and takes an optional selector to
-perform a find on it right away.
+Turns content into a L<Mojo::DOM> object and takes an optional selector to
+perform a C<find> on it right away, which returns a collection.
 Note that this method is EXPERIMENTAL and might change without warning!
 
 =head2 C<error>
@@ -784,13 +770,6 @@ Note that this method is EXPERIMENTAL and might change without warning!
 
 Parser errors and codes.
 
-=head2 C<finish>
-
-    $message->finish;
-
-Finish dynamic content generation.
-Note that this method is EXPERIMENTAL and might change without warning!
-
 =head2 C<fix_headers>
 
     $message = $message->fix_headers;
@@ -23,7 +23,7 @@ sub new {
     my $self = shift->SUPER::new();
 
     # Hash/Array
-    if (defined $_[1]) { $self->append(@_) }
+    if (@_ > 1) { $self->append(@_) }
 
     # String
     else { $self->parse(@_) }
@@ -214,10 +214,10 @@ sub to_string {
 
         # *( pchar / "/" / "?" ) with the exception of ";", "&" and "="
         encode $charset, $name if $charset;
-        url_escape $name, $Mojo::URL::PARAM;
+        url_escape $name, $Mojo::URL::UNRESERVED;
         if ($value) {
             encode $charset, $value if $charset;
-            url_escape $value, $Mojo::URL::PARAM;
+            url_escape $value, $Mojo::URL::UNRESERVED;
         }
 
         # Replace whitespace with "+"
@@ -306,9 +306,10 @@ Merge parameters.
 
 =head2 C<param>
 
-    my $foo = $params->param('foo');
-    my @foo = $params->param('foo');
-    my $foo = $params->param(foo => 'ba;r');
+    my @names = $params->param;
+    my $foo   = $params->param('foo');
+    my @foo   = $params->param('foo');
+    my $foo   = $params->param(foo => 'ba;r');
 
 Check parameter values.
 
@@ -134,6 +134,7 @@ L<Mojo::Server::CGI> is a simple and portable implementation of RFC 3875.
 
 L<Mojo::Server::CGI> inherits all attributes from L<Mojo::Server> and
 implements the following new ones.
+See L<Mojolicious::Guides::Cookbook> for deployment recipes.
 
 =head2 C<nph>
 
@@ -166,8 +166,11 @@ sub _build_tx {
 sub _drop {
     my ($self, $id) = @_;
 
-    # WebSocket
-    if (my $ws = $self->{_cs}->{$id}->{websocket}) { $ws->server_close }
+    # Connection
+    my $c = $self->{_cs}->{$id};
+
+    # Transaction
+    if (my $tx = $c->{websocket} || $c->{transaction}) { $tx->server_close }
 
     # Drop connection
     delete $self->{_cs}->{$id};
@@ -416,12 +419,12 @@ Mojo::Server::Daemon - Async IO HTTP 1.1 And WebSocket Server
 =head1 DESCRIPTION
 
 L<Mojo::Server::Daemon> is a full featured async io HTTP 1.1 and WebSocket
-server with C<IPv6>, C<TLS>, C<Bonjour>, C<epoll>, C<kqueue>, hot deployment
-and UNIX domain socket sharing support.
+server with C<TLS>, C<Bonjour>, C<epoll>, C<kqueue> and UNIX domain socket
+sharing support.
 
-Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::IP>,
-L<IO::Socket::SSL> and L<Net::Rendezvous::Publish> are supported
-transparently and used if installed.
+Optional modules L<IO::KQueue>, L<IO::Epoll>, L<IO::Socket::SSL> and
+L<Net::Rendezvous::Publish> are supported transparently and used if
+installed.
 
 =head1 ATTRIBUTES
 
@@ -458,8 +461,8 @@ Ports and files to listen on, defaults to C<http://*:3000>.
 
 =head2 C<listen_queue_size>
 
-    my $listen_queue_size = $daemon->listen_queue_zise;
-    $daemon               = $daemon->listen_queue_zise(128);
+    my $listen_queue_size = $daemon->listen_queue_size;
+    $daemon               = $daemon->listen_queue_size(128);
 
 Listen queue size, defaults to C<SOMAXCONN>.
 
@@ -422,6 +422,7 @@ Mojo::Server::FastCGI - FastCGI Server
 
 L<Mojo::Server::FastCGI> is a portable pure-Perl FastCGI implementation as
 described in the C<FastCGI Specification>.
+See L<Mojolicious::Guides::Cookbook> for deployment recipes.
 
 =head1 ATTRIBUTES
 
@@ -113,6 +113,7 @@ Mojo::Server::PSGI - PSGI Server
 
 L<Mojo::Server::PSGI> allows L<Mojo> applications to run on all PSGI
 compatible servers.
+See L<Mojolicious::Guides::Cookbook> for deployment recipes.
 
 =head1 METHODS
 
@@ -35,7 +35,7 @@ sub client_read {
     $self->{_state} = 'done' if $read == 0;
 
     # HEAD response
-    if ($req->method eq 'HEAD') {
+    if ($req->method =~ /^head$/i) {
         $res->parse_until_body($chunk);
         $self->{_state} = 'done' if $res->content->is_parsing_body;
     }
@@ -338,7 +338,7 @@ sub server_write {
         if ($self->{_write} <= 0) {
 
             # HEAD request
-            if ($req->method eq 'HEAD') {
+            if ($req->method =~ /^head$/i) {
 
                 # Don't send body if request method is HEAD
                 $self->{_state} = 'done';
@@ -19,12 +19,10 @@ our $UNRESERVED = 'A-Za-z0-9\-\.\_\~';
 our $SUBDELIM   = '!\$\&\'\(\)\*\+\,\;\=';
 our $PCHAR      = "$UNRESERVED$SUBDELIM\%\:\@";
 
-# The specs for this are blurry, it's mostly a collection of w3c suggestions
-our $PARAM = "$UNRESERVED\!\$\'\(\)\*\,\:\@\/\?";
-
 # IPv4 regex (RFC 3986)
 my $DEC_OCTET_RE = qr/(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/;
-our $IPV4_RE = qr/$DEC_OCTET_RE\.$DEC_OCTET_RE\.$DEC_OCTET_RE\.$DEC_OCTET_RE/;
+our $IPV4_RE =
+  qr/^$DEC_OCTET_RE\.$DEC_OCTET_RE\.$DEC_OCTET_RE\.$DEC_OCTET_RE$/;
 
 # IPv6 regex (RFC 3986)
 my $H16_RE  = qr/[0-9A-Fa-f]{1,4}/;
@@ -7,7 +7,6 @@ use base 'Mojo::Base';
 
 use Carp 'croak';
 use Mojo::Client;
-use Mojo::Commands;
 use Mojo::Home;
 use Mojo::Log;
 use Mojo::Transaction::HTTP;
@@ -53,20 +52,6 @@ sub new {
 
 sub handler { croak 'Method "handler" not implemented in subclass' }
 
-# Start command system
-sub start {
-    my $class = shift;
-
-    # We can be called on class or instance
-    $class = ref $class || $class;
-
-    # We are the application
-    $ENV{MOJO_APP} ||= $class;
-
-    # Start!
-    return Mojo::Commands->start(@_);
-}
-
 1;
 __END__
 
@@ -171,13 +156,6 @@ or L<Mojo::Transaction::WebSocket> object.
         my ($self, $tx) = @_;
     }
 
-=head2 C<start>
-
-    Mojo->start;
-    Mojo->start('daemon');
-
-Start the L<Mojo::Commands> command line interface for your application.
-
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
@@ -1,144 +0,0 @@
-package MojoX::Controller;
-
-use strict;
-use warnings;
-
-# Scalpel... blood bucket... priest.
-use base 'Mojo::Base';
-
-__PACKAGE__->attr('app');
-
-# Reserved stash values
-my $STASH_RE = qr/
-    ^
-    (?:
-    action
-    |
-    app
-    |
-    cb
-    |
-    class
-    |
-    controller
-    |
-    data
-    |
-    exception
-    |
-    extends
-    |
-    format
-    |
-    handler
-    |
-    json
-    |
-    layout
-    |
-    method
-    |
-    namespace
-    |
-    partial
-    |
-    path
-    |
-    status
-    |
-    template
-    |
-    text
-    )
-    $
-    /x;
-
-# I'm immortal.
-# How come you scream so much when you're in danger?
-# I never said I wasn't a drama queen.
-sub render_exception { }
-sub render_not_found { }
-
-# All this knowledge is giving me a raging brainer.
-sub stash {
-    my $self = shift;
-
-    # Initialize
-    $self->{stash} ||= {};
-
-    # Hash
-    return $self->{stash} unless @_;
-
-    # Get
-    return $self->{stash}->{$_[0]} unless @_ > 1 || ref $_[0];
-
-    # Set
-    my $values = ref $_[0] ? $_[0] : {@_};
-    for my $key (keys %$values) {
-        $self->app->log->debug(qq/Careful, "$key" is a reserved stash value./)
-          if $key =~ $STASH_RE;
-        $self->{stash}->{$key} = $values->{$key};
-    }
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Controller - Controller Base Class
-
-=head1 SYNOPSIS
-
-    use base 'MojoX::Controller';
-
-=head1 DESCRIPTION
-
-L<MojoX::Controller> is an abstract controllers base class.
-
-=head1 L<MojoX::Controller> implements the following attributes.
-
-=head2 C<app>
-
-    my $app = $c->app;
-    $c      = $c->app(MojoSubclass->new);
-
-A reference back to the application that dispatched to this controller.
-
-=head1 METHODS
-
-L<MojoX::Controller> inherits all methods from L<Mojo::Base> and implements
-the following new ones.
-
-=head2 C<render_exception>
-
-    $c->render_exception($e);
-
-Turn exception into output.
-
-=head2 C<render_not_found>
-
-    $c->render_not_found;
-
-Default output.
-
-=head2 C<stash>
-
-    my $stash = $c->stash;
-    my $foo   = $c->stash('foo');
-    $c        = $c->stash({foo => 'bar'});
-    $c        = $c->stash(foo => 'bar');
-
-Non persistent data storage and exchange.
-
-    $c->stash->{foo} = 'bar';
-    my $foo = $c->stash->{foo};
-    delete $c->stash->{foo};
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,66 +0,0 @@
-package MojoX::Dispatcher::Routes::Controller;
-
-use strict;
-use warnings;
-
-use base 'MojoX::Session::Cookie::Controller';
-
-__PACKAGE__->attr('match');
-
-# Just make a simple cake. And this time, if someone's going to jump out of
-# it make sure to put them in *after* you cook it.
-sub param {
-    my ($self, $name) = @_;
-
-    # Captures
-    my $p = $self->stash->{'mojo.captures'} || {};
-    return $p->{$name} if exists $p->{$name};
-
-    # Params
-    return $self->req->param($name);
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Dispatcher::Routes::Controller - Controller Base Class
-
-=head1 SYNOPSIS
-
-    use base 'MojoX::Dispatcher::Routes::Controller';
-
-=head1 DESCRIPTION
-
-L<MojoX::Dispatcher::Routes::Controller> is a controller base class.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Dispatcher::Routes::Controller> inherits all attributes from
-L<MojoX::Session::Cookie::Controller> implements the following attributes.
-
-=head2 C<match>
-
-    my $m = $c->match;
-
-A L<MojoX::Routes::Match> object containing the routes results for the
-current request.
-
-=head1 METHODS
-
-L<MojoX::Dispatcher::Routes::Controller> inherits all methods from
-L<MojoX::Session::Cookie::Controller> and implements the following new ones.
-
-=head2 C<param>
-
-    my $param  = $c->param('foo');
-    my @params = $c->param('foo');
-
-Request parameters and routes captures.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,422 +0,0 @@
-package MojoX::Dispatcher::Routes;
-
-use strict;
-use warnings;
-
-use base 'MojoX::Routes';
-
-use Mojo::Exception;
-use Mojo::Loader;
-use Mojo::Util 'camelize';
-use MojoX::Routes::Match;
-use Scalar::Util 'weaken';
-
-__PACKAGE__->attr(
-    controller_base_class => 'MojoX::Dispatcher::Routes::Controller');
-__PACKAGE__->attr(hidden => sub { [qw/new app attr render req res stash tx/] }
-);
-__PACKAGE__->attr('namespace');
-
-# Hey. What kind of party is this? There's no booze and only one hooker.
-sub auto_render {
-    my ($self, $c) = @_;
-
-    # Transaction
-    my $tx = $c->tx;
-
-    # Rendering
-    my $success = eval {
-
-        # Render
-        $c->render unless $c->stash->{'mojo.rendered'} || $tx->is_websocket;
-
-        # Success
-        1;
-    };
-
-    # Renderer error
-    $c->render_exception($@) if !$success && $@;
-
-    # Rendered
-    return;
-}
-
-sub detour {
-    my $self = shift;
-
-    # Partial
-    $self->partial('path');
-
-    # Defaults
-    $self->to(@_);
-
-    return $self;
-}
-
-sub dispatch {
-    my ($self, $c) = @_;
-
-    # Response
-    my $res = $c->res;
-
-    # Already rendered
-    return if $res->code;
-
-    # Path
-    my $path = $c->stash->{path};
-    $path = "/$path" if defined $path && $path !~ /^\//;
-
-    # Match
-    my $m = MojoX::Routes::Match->new($c, $path);
-    $m->match($self);
-    $c->match($m);
-
-    # No match
-    return 1 unless $m && @{$m->stack};
-
-    # Status
-    unless ($res->code) {
-
-        # Websocket handshake
-        $res->code(101) if !$res->code && $c->tx->is_websocket;
-
-        # Error or 200
-        my ($error, $code) = $c->req->error;
-        $res->code($code) if $code;
-    }
-
-    # Walk the stack
-    return 1 if $self->_walk_stack($c);
-
-    # Render
-    return $self->auto_render($c);
-}
-
-sub hide { push @{shift->hidden}, @_ }
-
-sub _dispatch_callback {
-    my ($self, $c, $staging) = @_;
-
-    # Debug
-    $c->app->log->debug(qq/Dispatching callback./);
-
-    # Dispatch
-    my $continue;
-    my $cb      = $c->match->captures->{cb};
-    my $success = eval {
-
-        # Callback
-        $continue = $cb->($c);
-
-        # Success
-        1;
-    };
-
-    # Callback error
-    if (!$success && $@) {
-        my $e = Mojo::Exception->new($@);
-        $c->app->log->error($e);
-        return $e;
-    }
-
-    # Success!
-    return 1 unless $staging;
-    return 1 if $continue;
-
-    return;
-}
-
-sub _dispatch_controller {
-    my ($self, $c, $staging) = @_;
-
-    # Application
-    my $app = $c->match->captures->{app};
-
-    # Class
-    $app ||= $self->_generate_class($c);
-    return 1 unless $app;
-
-    # Method
-    my $method = $self->_generate_method($c);
-
-    # Debug
-    my $dispatch = ref $app || $app;
-    $dispatch .= "->$method" if $method;
-    $c->app->log->debug("Dispatching $dispatch.");
-
-    # Load class
-    unless (ref $app && $self->{_loaded}->{$app}) {
-
-        # Load
-        if (my $e = Mojo::Loader->load($app)) {
-
-            # Doesn't exist
-            unless (ref $e) {
-                $c->app->log->debug("$app does not exist, maybe a typo?");
-                return;
-            }
-
-            # Error
-            $c->app->log->error($e);
-            return $e;
-        }
-
-        # Loaded
-        $self->{_loaded}->{$app}++;
-    }
-
-    # Dispatch
-    my $continue;
-    my $success = eval {
-
-        # Instantiate
-        $app = $app->new($c) unless ref $app;
-
-        # Action
-        if ($method && $app->isa($self->controller_base_class)) {
-
-            # Call action
-            $continue = $app->$method if $app->can($method);
-
-            # Merge stash
-            my $new = $app->stash;
-            @{$c->stash}{keys %$new} = values %$new;
-        }
-
-        # Handler
-        elsif ($app->isa('Mojo')) {
-
-            # Connect routes
-            if ($app->can('routes')) {
-                my $r = $app->routes;
-                unless ($r->parent) {
-                    $r->parent($c->match->endpoint);
-                    weaken $r->{parent};
-                }
-            }
-
-            # Handler
-            $app->handler($c);
-        }
-
-        # Success
-        1;
-    };
-
-    # Controller error
-    if (!$success && $@) {
-        my $e = Mojo::Exception->new($@);
-        $c->app->log->error($e);
-        return $e;
-    }
-
-    # Success!
-    return 1 unless $staging;
-    return 1 if $continue;
-
-    return;
-}
-
-sub _generate_class {
-    my ($self, $c) = @_;
-
-    # Field
-    my $field = $c->match->captures;
-
-    # Class
-    my $class = $field->{class};
-    my $controller = $field->{controller} || '';
-    unless ($class) {
-        $class = $controller;
-        camelize $class;
-    }
-
-    # Namespace
-    my $namespace = $field->{namespace};
-    $namespace = $self->namespace unless defined $namespace;
-    $class = length $class ? "${namespace}::$class" : $namespace
-      if length $namespace;
-
-    # Invalid
-    return unless $class =~ /^[a-zA-Z0-9_:]+$/;
-
-    return $class;
-}
-
-sub _generate_method {
-    my ($self, $c) = @_;
-
-    # Field
-    my $field = $c->match->captures;
-
-    # Prepare hidden
-    unless ($self->{_hidden}) {
-        $self->{_hidden} = {};
-        $self->{_hidden}->{$_}++ for @{$self->hidden};
-    }
-
-    my $method = $field->{method};
-    $method ||= $field->{action};
-
-    # Shortcut
-    return unless $method;
-
-    # Shortcut for hidden methods
-    if ($self->{_hidden}->{$method} || index($method, '_') == 0) {
-        $c->app->log->debug(qq/Action "$method" is not allowed./);
-        return;
-    }
-
-    # Invalid
-    unless ($method =~ /^[a-zA-Z0-9_:]+$/) {
-        $c->app->log->debug(qq/Action "$method" is invalid./);
-        return;
-    }
-
-    return $method;
-}
-
-sub _walk_stack {
-    my ($self, $c) = @_;
-
-    # Stack
-    my $stack = $c->match->stack;
-
-    # Walk the stack
-    my $staging = @$stack;
-    for my $field (@$stack) {
-        $staging--;
-
-        # Stash
-        my $stash = $c->stash;
-
-        # Captures
-        my $captures = $stash->{'mojo.captures'} ||= {};
-        $stash->{'mojo.captures'} = {%$captures, %$field};
-
-        # Merge in captures
-        @{$c->stash}{keys %$field} = values %$field;
-
-        # Captures
-        $c->match->captures($field);
-
-        # Dispatch
-        my $e =
-            $field->{cb}
-          ? $self->_dispatch_callback($c, $staging)
-          : $self->_dispatch_controller($c, $staging);
-
-        # Exception
-        if (ref $e) {
-            $c->render_exception($e);
-            return 1;
-        }
-
-        # Break the chain
-        return 1 if $staging && !$e;
-    }
-
-    # Done
-    return;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Dispatcher::Routes - Routes Dispatcher
-
-=head1 SYNOPSIS
-
-    use MojoX::Dispatcher::Routes;
-
-    # New dispatcher
-    my $dispatcher = MojoX::Dispatcher::Routes->new;
-
-    # Dispatch
-    $dispatcher->dispatch(MojoX::Dispatcher::Routes::Controller->new);
-
-=head1 DESCRIPTION
-
-L<MojoX::Dispatcher::Routes> is a L<MojoX::Routes> based dispatcher.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Dispatcher::Routes> inherits all attributes from L<MojoX::Routes>
-and implements the following ones.
-
-=head2 C<controller_base_class>
-
-    my $base    = $dispatcher->controller_base_class;
-    $dispatcher = $dispatcher->controller_base_class(
-        'MojoX::Dispatcher::Routes::Controller'
-    );
-
-Base class used to identify controllers, defaults to
-L<MojoX::Dispatcher::Routes::Controller>.
-
-=head2 C<hidden>
-
-    my $hidden  = $dispatcher->hidden;
-    $dispatcher = $dispatcher->hidden(
-        [qw/new attr tx render req res stash/]
-    );
-
-Methods and attributes that are hidden from the dispatcher.
-
-=head2 C<namespace>
-
-    my $namespace = $dispatcher->namespace;
-    $dispatcher   = $dispatcher->namespace('Foo::Bar::Controller');
-
-Namespace to search for controllers.
-
-=head1 METHODS
-
-L<MojoX::Dispatcher::Routes> inherits all methods from L<MojoX::Routes> and
-implements the following ones.
-
-=head2 C<auto_render>
-
-    $dispatcher->auto_render(MojoX::Dispatcher::Routes::Controller->new);
-
-Automatic rendering.
-
-=head2 C<detour>
-
-    $dispatcher = $dispatcher->detour(action => 'foo');
-    $dispatcher = $dispatcher->detour({action => 'foo'});
-    $dispatcher = $dispatcher->detour('controller#action');
-    $dispatcher = $dispatcher->detour('controller#action', foo => 'bar');
-    $dispatcher = $dispatcher->detour('controller#action', {foo => 'bar'});
-    $dispatcher = $dispatcher->detour($app);
-    $dispatcher = $dispatcher->detour($app, foo => 'bar');
-    $dispatcher = $dispatcher->detour($app, {foo => 'bar'});
-    $dispatcher = $dispatcher->detour('MyApp');
-    $dispatcher = $dispatcher->detour('MyApp', foo => 'bar');
-    $dispatcher = $dispatcher->detour('MyApp', {foo => 'bar'});
-
-Set default parameters for this route and allow partial matching to simplify
-application embedding.
-Note that this method is EXPERIMENTAL and might change without warning!
-
-=head2 C<dispatch>
-
-    my $e = $dispatcher->dispatch(
-        MojoX::Dispatcher::Routes::Controller->new
-    );
-
-Match routes and dispatch.
-
-=head2 C<hide>
-
-    $dispatcher = $dispatcher->hide('new');
-
-Hide method or attribute from the dispatcher.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,361 +0,0 @@
-package MojoX::Dispatcher::Static;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use File::stat;
-use File::Spec;
-use Mojo::Asset::File;
-use Mojo::Asset::Memory;
-use Mojo::Command;
-use Mojo::Content::Single;
-use Mojo::Path;
-use MojoX::Types;
-
-__PACKAGE__->attr([qw/default_static_class prefix root/]);
-__PACKAGE__->attr(types => sub { MojoX::Types->new });
-
-# Valentine's Day's coming? Aw crap! I forgot to get a girlfriend again!
-sub dispatch {
-    my ($self, $c) = @_;
-
-    # Already rendered
-    return if $c->res->code;
-
-    # Canonical path
-    my $path = $c->req->url->path->clone->canonicalize->to_string;
-
-    # Prefix
-    if (my $prefix = $self->prefix) {
-        return 1 unless $path =~ s/^$prefix//;
-    }
-
-    # Parts
-    my @parts = @{Mojo::Path->new->parse($path)->parts};
-
-    # Shortcut
-    return 1 unless @parts;
-
-    # Prevent directory traversal
-    return 1 if $parts[0] eq '..';
-
-    # Serve static file
-    return $self->serve($c, join('/', @parts));
-}
-
-sub serve {
-    my ($self, $c, $rel) = @_;
-
-    # Append path to root
-    my $path = File::Spec->catfile($self->root, split('/', $rel));
-
-    # Extension
-    $path =~ /\.(\w+)$/;
-    my $ext = $1;
-
-    # Type
-    my $type = $self->types->type($ext) || 'text/plain';
-
-    # Response
-    my $res = $c->res;
-
-    # Asset
-    my $asset;
-
-    # Modified
-    my $modified = $self->{_modified} ||= time;
-
-    # Size
-    my $size = 0;
-
-    # File
-    if (-f $path) {
-
-        # Readable
-        if (-r $path) {
-
-            # Modified
-            my $stat = stat($path);
-            $modified = $stat->mtime;
-
-            # Size
-            $size = $stat->size;
-
-            # Content
-            $asset = Mojo::Asset::File->new(path => $path);
-        }
-
-        # Exists, but is forbidden
-        else {
-            $c->app->log->debug('File forbidden.');
-            $res->code(403) and return;
-        }
-    }
-
-    # Inline file
-    elsif (defined(my $file = $self->_get_inline_file($c, $rel))) {
-        $size  = length $file;
-        $asset = Mojo::Asset::Memory->new->add_chunk($file);
-    }
-
-    # Found
-    if ($asset) {
-
-        # Log
-        $c->app->log->debug(qq/Serving static file "$rel"./);
-
-        # Resume
-        $c->tx->resume;
-
-        # Request
-        my $req = $c->req;
-
-        # Request headers
-        my $rqh = $req->headers;
-
-        # Response headers
-        my $rsh = $res->headers;
-
-        # If modified since
-        if (my $date = $rqh->if_modified_since) {
-
-            # Not modified
-            my $since = Mojo::Date->new($date)->epoch;
-            if (defined $since && $since == $modified) {
-                $c->app->log->debug('File not modified.');
-                $res->code(304);
-                $rsh->remove('Content-Type');
-                $rsh->remove('Content-Length');
-                $rsh->remove('Content-Disposition');
-                return;
-            }
-        }
-
-        # Start and end
-        my $start = 0;
-        my $end = $size - 1 >= 0 ? $size - 1 : 0;
-
-        # Range
-        if (my $range = $rqh->range) {
-            if ($range =~ m/^bytes=(\d+)\-(\d+)?/ && $1 <= $end) {
-                $start = $1;
-                $end = $2 if defined $2 && $2 <= $end;
-                $res->code(206);
-                $rsh->content_length($end - $start + 1);
-                $rsh->content_range("bytes $start-$end/$size");
-                $c->app->log->debug("Range request: $start-$end/$size.");
-            }
-            else {
-
-                # Not satisfiable
-                $res->code(416);
-                return;
-            }
-        }
-        $asset->start_range($start);
-        $asset->end_range($end);
-
-        # Response
-        $res->code(200) unless $res->code;
-        $res->content->asset($asset);
-        $rsh->content_type($type);
-        $rsh->accept_ranges('bytes');
-        $rsh->last_modified(Mojo::Date->new($modified));
-        return;
-    }
-
-    return 1;
-}
-
-sub serve_404 { shift->serve_error(shift, 404) }
-
-sub serve_500 { shift->serve_error(shift, 500) }
-
-sub serve_error {
-    my ($self, $c, $code, $rel) = @_;
-
-    # Shortcut
-    return 1 unless $c && $code;
-
-    my $res = $c->res;
-
-    # Render once
-    return if ($res->code || '') eq $code;
-
-    # Code
-    $res->code($code);
-
-    # Default to "code.html"
-    $rel ||= "$code.html";
-
-    # File
-    if (!$self->serve($c, $rel)) {
-
-        # Log
-        $c->app->log->debug(qq/Serving error file "$rel"./);
-    }
-
-    # 404
-    elsif ($code == 404) {
-
-        # Log
-        $c->app->log->debug('Serving 404 error.');
-
-        $res->headers->content_type('text/html');
-        $res->body(<<'EOF');
-<!doctype html><html>
-    <head><title>File Not Found</title></head>
-    <body><h2>File Not Found</h2></body>
-</html>
-EOF
-    }
-
-    # Error
-    else {
-
-        # Log
-        $c->app->log->debug(qq/Serving error "$code"./);
-
-        $res->headers->content_type('text/html');
-        $res->body(<<'EOF');
-<!doctype html><html>
-    <head><title>Internal Server Error</title></head>
-    <body><h2>Internal Server Error</h2></body>
-</html>
-EOF
-    }
-
-    return;
-}
-
-sub _get_inline_file {
-    my ($self, $c, $rel) = @_;
-
-    # Protect templates
-    return if $rel =~ /\.\w+\.\w+$/;
-
-    # Class
-    my $class =
-         $c->stash->{static_class}
-      || $ENV{MOJO_STATIC_CLASS}
-      || $self->default_static_class
-      || 'main';
-
-    # Inline files
-    my $inline = $self->{_inline_files}->{$class};
-    unless ($inline) {
-        my $files = Mojo::Command->new->get_all_data($class) || {};
-        $inline = $self->{_inline_files}->{$class} = [keys %$files];
-    }
-
-    # Find
-    for my $path (@$inline) {
-        return Mojo::Command->new->get_data($path, $class) if $path eq $rel;
-    }
-
-    # Nothing
-    return;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Dispatcher::Static - Serve Static Files
-
-=head1 SYNOPSIS
-
-    use MojoX::Dispatcher::Static;
-
-    # New dispatcher
-    my $dispatcher = MojoX::Dispatcher::Static->new(
-        prefix => '/images',
-        root   => '/ftp/pub/images'
-    );
-
-    # Dispatch
-    my $success = $dispatcher->dispatch($c);
-
-=head1 DESCRIPTION
-
-L<MojoX::Dispatcher::Static> is a dispatcher for static files with C<Range>
-and C<If-Modified-Since> support.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Dispatcher::Static> implements the following attributes.
-
-=head2 C<default_static_class>
-
-    my $class   = $dispatcher->default_static_class;
-    $dispatcher = $dispatcher->default_static_class('main');
-
-The dispatcher will use this class to look for files in the C<DATA> section.
-
-=head2 C<prefix>
-
-    my $prefix  = $dispatcher->prefix;
-    $dispatcher = $dispatcher->prefix('/static');
-
-Prefix path to remove from incoming paths before dispatching.
-
-=head2 C<types>
-
-    my $types   = $dispatcher->types;
-    $dispatcher = $dispatcher->types(MojoX::Types->new);
-
-MIME types, by default a L<MojoX::Types> object.
-
-=head2 C<root>
-
-    my $root    = $dispatcher->root;
-    $dispatcher = $dispatcher->root('/foo/bar/files');
-
-Directory to serve static files from.
-
-=head1 METHODS
-
-L<MojoX::Dispatcher::Static> inherits all methods from L<Mojo::Base> and
-implements the following ones.
-
-=head2 C<dispatch>
-
-    my $success = $dispatcher->dispatch($c);
-
-Dispatch a L<MojoX::Controller> object.
-
-=head2 C<serve>
-
-    my $success = $dispatcher->serve($c, 'foo/bar.html');
-
-Serve a specific file.
-
-=head2 C<serve_404>
-
-    my $success = $dispatcher->serve_404($c);
-    my $success = $dispatcher->serve_404($c, '404.html');
-
-Serve a C<404> error page, guaranteed to render at least a default page.
-
-=head2 C<serve_500>
-
-    my $success = $dispatcher->serve_500($c);
-    my $success = $dispatcher->serve_500($c, '500.html');
-
-Serve a C<500> error page, guaranteed to render at least a default page.
-
-=head2 C<serve_error>
-
-    my $success = $dispatcher->serve_error($c, 404);
-    my $success = $dispatcher->serve_error($c, 404, '404.html');
-
-Serve error page, guaranteed to render at least a default page.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,514 +0,0 @@
-package MojoX::Renderer;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use File::Spec;
-use Mojo::ByteStream 'b';
-use Mojo::Command;
-use Mojo::Home;
-use Mojo::JSON;
-use MojoX::Types;
-use Mojo::Util 'encode';
-
-__PACKAGE__->attr(default_format => 'html');
-__PACKAGE__->attr([qw/default_handler default_template_class/]);
-__PACKAGE__->attr(detect_templates => 1);
-__PACKAGE__->attr(encoding         => 'UTF-8');
-__PACKAGE__->attr(handler          => sub { {} });
-__PACKAGE__->attr(helper           => sub { {} });
-__PACKAGE__->attr(layout_prefix    => 'layouts');
-__PACKAGE__->attr(root             => '/');
-__PACKAGE__->attr(types            => sub { MojoX::Types->new });
-
-# This is not how Xmas is supposed to be.
-# In my day Xmas was about bringing people together, not blowing them apart.
-sub new {
-    my $self = shift->SUPER::new(@_);
-
-    # Data
-    $self->add_handler(
-        data => sub {
-            my ($r, $c, $output, $options) = @_;
-            $$output = $options->{data};
-        }
-    );
-
-    # JSON
-    $self->add_handler(
-        json => sub {
-            my ($r, $c, $output, $options) = @_;
-            $$output = Mojo::JSON->new->encode($options->{json});
-        }
-    );
-
-    # Text
-    $self->add_handler(
-        text => sub {
-            my ($r, $c, $output, $options) = @_;
-            $$output = $options->{text};
-        }
-    );
-
-    return $self;
-}
-
-sub add_handler {
-    my ($self, $name, $cb) = @_;
-    $self->handler->{$name} = $cb;
-    return $self;
-}
-
-sub add_helper {
-    my ($self, $name, $cb) = @_;
-    $self->helper->{$name} = $cb;
-    return $self;
-}
-
-sub get_inline_template {
-    my ($self, $options, $template) = @_;
-    return Mojo::Command->new->get_data($template,
-        $self->_detect_template_class($options));
-}
-
-# Bodies are for hookers and fat people.
-sub render {
-    my ($self, $c, $args) = @_;
-
-    # Stash
-    my $stash = $c->stash;
-
-    # Arguments
-    $args ||= {};
-
-    # Content
-    my $content = $stash->{'mojo.content'} ||= {};
-
-    # Partial
-    my $partial = $args->{partial};
-
-    # Localize extends and layout
-    local $stash->{layout}  = $partial ? undef : $stash->{layout};
-    local $stash->{extends} = $partial ? undef : $stash->{extends};
-
-    # Merge stash and arguments
-    while (my ($key, $value) = each %$args) {
-        $stash->{$key} = $value;
-    }
-
-    # Template
-    my $template = delete $stash->{template};
-
-    # Template class
-    my $class = $stash->{template_class};
-
-    # Format
-    my $format = $stash->{format} || $self->default_format;
-
-    # Handler
-    my $handler = $stash->{handler};
-
-    # Data
-    my $data = delete $stash->{data};
-
-    # JSON
-    my $json = delete $stash->{json};
-
-    # Text
-    my $text = delete $stash->{text};
-
-    # Inline
-    my $inline = delete $stash->{inline};
-    $handler = $self->default_handler if defined $inline && !defined $handler;
-
-    my $options = {
-        template       => $template,
-        format         => $format,
-        handler        => $handler,
-        encoding       => $self->encoding,
-        inline         => $inline,
-        template_class => $class
-    };
-    my $output;
-
-    # Text
-    if (defined $text) {
-
-        # Render
-        $self->handler->{text}->($self, $c, \$output, {text => $text});
-
-        # Extends
-        $content->{content} = b("$output")
-          if ($c->stash->{extends} || $c->stash->{layout});
-    }
-
-    # Data
-    elsif (defined $data) {
-
-        # Render
-        $self->handler->{data}->($self, $c, \$output, {data => $data});
-
-        # Extends
-        $content->{content} = b("$output")
-          if ($c->stash->{extends} || $c->stash->{layout});
-    }
-
-    # JSON
-    elsif (defined $json) {
-
-        # Render
-        $self->handler->{json}->($self, $c, \$output, {json => $json});
-        $format = 'json';
-
-        # Extends
-        $content->{content} = b("$output")
-          if ($c->stash->{extends} || $c->stash->{layout});
-    }
-
-    # Template or templateless handler
-    else {
-
-        # Render
-        return unless $self->_render_template($c, \$output, $options);
-
-        # Extends
-        $content->{content} = b("$output")
-          if ($c->stash->{extends} || $c->stash->{layout});
-    }
-
-    # Extends
-    while ((my $extends = $self->_extends($c)) && !$json && !$data) {
-
-        # Stash
-        my $stash = $c->stash;
-
-        # Template class
-        $class = $stash->{template_class};
-        $options->{template_class} = $class;
-
-        # Handler
-        $handler = $stash->{handler};
-        $options->{handler} = $handler;
-
-        # Format
-        $format = $stash->{format} || $self->default_format;
-        $options->{format} = $format;
-
-        # Template
-        $options->{template} = $extends;
-
-        # Render
-        $self->_render_template($c, \$output, $options);
-    }
-
-    # Encoding (JSON is already encoded)
-    unless ($partial) {
-        my $encoding = $options->{encoding};
-        encode $encoding, $output if $encoding && $output && !$json && !$data;
-    }
-
-    # Type
-    my $type = $self->types->type($format) || 'text/plain';
-
-    return ($output, $type);
-}
-
-sub template_name {
-    my ($self, $options) = @_;
-
-    # Template
-    return unless my $template = $options->{template} || '';
-
-    # Format
-    return unless my $format = $options->{format};
-
-    # Handler
-    my $handler = $options->{handler};
-
-    # File
-    my $file = "$template.$format";
-    $file = "$file.$handler" if $handler;
-
-    return $file;
-}
-
-sub template_path {
-    my $self = shift;
-    return unless my $name = $self->template_name(shift);
-    return File::Spec->catfile($self->root, split '/', $name);
-}
-
-sub _detect_handler {
-    my ($self, $options) = @_;
-
-    # Disabled
-    return unless $self->detect_templates;
-
-    # Template class
-    my $class = $self->_detect_template_class($options);
-
-    # Templates
-    my $templates = $self->{_templates};
-    unless ($templates) {
-        $templates = $self->{_templates} =
-          Mojo::Home->new->parse($self->root)->list_files;
-    }
-
-    # Inline templates
-    my $inline = $self->{_inline_templates}->{$class}
-      ||= $self->_list_inline_templates($class);
-
-    # Detect
-    return unless my $file = $self->template_name($options);
-    $file = quotemeta $file;
-    for my $template (@$templates, @$inline) {
-        if ($template =~ /^$file\.(\w+)$/) { return $1 }
-    }
-
-    return;
-}
-
-# You are hereby conquered.
-# Please line up in order of how much beryllium it takes to kill you.
-sub _detect_template_class {
-    my ($self, $options) = @_;
-    return
-         $options->{template_class}
-      || $ENV{MOJO_TEMPLATE_CLASS}
-      || $self->default_template_class
-      || 'main';
-}
-
-sub _extends {
-    my ($self, $c) = @_;
-
-    # Layout
-    my $stash = $c->stash;
-    if (my $layout = delete $stash->{layout}) {
-        $stash->{extends} ||= $self->layout_prefix . '/' . $layout;
-    }
-
-    # Extends
-    return delete $stash->{extends};
-}
-
-sub _list_inline_templates {
-    my ($self, $class) = @_;
-
-    # Get all
-    my $all = Mojo::Command->new->get_all_data($class);
-
-    # List
-    return [keys %$all];
-}
-
-# Well, at least here you'll be treated with dignity.
-# Now strip naked and get on the probulator.
-sub _render_template {
-    my ($self, $c, $output, $options) = @_;
-
-    # Renderer
-    my $handler =
-         $options->{handler}
-      || $self->_detect_handler($options)
-      || $self->default_handler;
-    $options->{handler} = $handler;
-    my $renderer = $self->handler->{$handler};
-
-    # No handler
-    unless ($renderer) {
-        $c->app->log->error(qq/No handler for "$handler" available./);
-        return;
-    }
-
-    # Render
-    return unless $renderer->($self, $c, $output, $options);
-
-    # Success!
-    return 1;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Renderer - MIME Type Based Renderer
-
-=head1 SYNOPSIS
-
-    use MojoX::Renderer;
-
-    my $renderer = MojoX::Renderer->new;
-
-=head1 DESCRIPTION
-
-L<MojoX::Renderer> is the standard L<Mojolicious> renderer.
-It turns your stashed data structures into content.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Types> implements the following attributes.
-
-=head2 C<default_format>
-
-    my $default = $renderer->default_format;
-    $renderer   = $renderer->default_format('html');
-
-The default format to render if C<format> is not set in the stash.
-The renderer will use L<MojoX::Types> to look up the content MIME type.
-
-=head2 C<default_handler>
-
-    my $default = $renderer->default_handler;
-    $renderer   = $renderer->default_handler('epl');
-
-The default template handler to use for rendering in cases where auto
-detection doesn't work, like for C<inline> templates.
-
-=over 4
-
-=item epl
-
-C<Embedded Perl Lite> handled by L<Mojolicious::Plugin::EplRenderer>.
-
-=item ep
-
-C<Embedded Perl> handled by L<Mojolicious::Plugin::EpRenderer>.
-
-=back
-
-=head2 C<default_template_class>
-
-    my $default = $renderer->default_template_class;
-    $renderer   = $renderer->default_template_class('main');
-
-The renderer will use this class to look for templates in the C<DATA>
-section.
-
-=head2 C<detect_templates>
-
-    my $detect = $renderer->detect_templates;
-    $renderer  = $renderer->detect_templates(1);
-
-Template auto detection, the renderer will try to select the right template
-and renderer automatically.
-
-=head2 C<encoding>
-
-    my $encoding = $renderer->encoding;
-    $renderer    = $renderer->encoding('koi8-r');
-
-Will encode the content if set, defaults to C<UTF-8>.
-
-=head2 C<handler>
-
-    my $handler = $renderer->handler;
-    $renderer   = $renderer->handler({epl => sub { ... }});
-
-Registered handlers.
-
-=head2 C<helper>
-
-    my $helper = $renderer->helper;
-    $renderer  = $renderer->helper({url_for => sub { ... }});
-
-Registered helpers.
-
-=head2 C<layout_prefix>
-
-    my $prefix = $renderer->layout_prefix;
-    $renderer  = $renderer->layout_prefix('layouts');
-
-Directory to look for layouts in, defaults to C<layouts>.
-
-=head2 C<root>
-
-   my $root  = $renderer->root;
-   $renderer = $renderer->root('/foo/bar/templates');
-   
-Directory to look for templates in.
-
-=head2 C<types>
-
-    my $types = $renderer->types;
-    $renderer = $renderer->types(MojoX::Types->new);
-
-L<MojoX::Types> object to use for looking up MIME types.
-
-=head1 METHODS
-
-L<MojoX::Renderer> inherits all methods from L<Mojo::Base> and implements the
-following ones.
-
-=head2 C<new>
-
-    my $renderer = MojoX::Renderer->new;
-
-Construct a new renderer.
-
-=head2 C<add_handler>
-
-    $renderer = $renderer->add_handler(epl => sub { ... });
-    
-Add a new handler to the renderer.
-See L<Mojolicious::Plugin::EpRenderer> for a sample renderer.
-
-=head2 C<add_helper>
-
-    $renderer = $renderer->add_helper(url_for => sub { ... });
-
-Add a new helper to the renderer.
-See L<Mojolicious::Plugin::EpRenderer> for sample helpers.
-
-=head2 C<get_inline_template>
-
-    my $template = $renderer->get_inline_template({
-        template       => 'foo/bar',
-        format         => 'html',
-        handler        => 'epl'
-        template_class => 'main'
-    }, 'foo.html.ep');
-
-Get an inline template by name, usually used by handlers.
-
-=head2 C<render>
-
-    my ($output, $type) = $renderer->render($c);
-    my ($output, $type) = $renderer->render($c, $args);
-
-Render output through one of the Mojo renderers.
-This renderer requires some configuration, at the very least you will need to
-have a default C<format> and a default C<handler> as well as a C<template> or
-C<text>/C<json>.
-See L<Mojolicious::Controller> for a more user friendly interface.
-
-=head2 C<template_name>
-
-    my $template = $renderer->template_name({
-        template => 'foo/bar',
-        format   => 'html',
-        handler  => 'epl'
-    });
-    
-Builds a template name based on an options hash with C<template>, C<format>
-and C<handler>.
-
-=head2 C<template_path>
-
-    my $path = $renderer->template_path({
-        template => 'foo/bar',
-        format   => 'html',
-        handler  => 'epl'
-    });
-
-Builds a full template path based on an options hash with C<template>,
-C<format> and C<handler>.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,327 +0,0 @@
-package MojoX::Routes::Match;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use Carp 'croak';
-use Mojo::Util qw/decode url_unescape/;
-use Mojo::URL;
-use Scalar::Util 'weaken';
-
-__PACKAGE__->attr(captures => sub { {} });
-__PACKAGE__->attr([qw/endpoint root/]);
-__PACKAGE__->attr(stack => sub { [] });
-
-# I'm Bender, baby, please insert liquor!
-sub new {
-    my $self = shift->SUPER::new();
-    my $c    = shift;
-
-    # Controller
-    $self->{_controller} = $c;
-    weaken $self->{_controller};
-
-    # Path
-    unless ($self->{_path} = shift) {
-        $self->{_path} = $c->req->url->path->to_string;
-        url_unescape $self->{_path};
-        decode 'UTF8', $self->{_path};
-    }
-
-    return $self;
-}
-
-# Life can be hilariously cruel.
-sub match {
-    my ($self, $r) = @_;
-
-    # Shortcut
-    return unless $r;
-
-    # Dictionary
-    my $dictionary = $self->{_dictionary} ||= $r->dictionary;
-
-    # Root
-    $self->root($r) unless $self->root;
-
-    # Path
-    my $path = $self->{_path};
-
-    # Match
-    my $captures = $r->pattern->shape_match(\$path);
-
-    # No match
-    return unless $captures;
-
-    # Conditions
-    for (my $i = 0; $i < @{$r->conditions}; $i += 2) {
-        my $name      = $r->conditions->[$i];
-        my $value     = $r->conditions->[$i + 1];
-        my $condition = $dictionary->{$name};
-
-        # No condition
-        return unless $condition;
-
-        # Match
-        return
-          if !$condition->($r, $self->{_controller}, $self->captures, $value);
-    }
-
-    # Partial
-    if (my $partial = $r->partial) {
-        $captures->{$partial} = $path;
-        $path = '';
-    }
-    $self->{_path} = $path;
-
-    # Merge captures
-    $captures = {%{$self->captures}, %$captures};
-    $self->captures($captures);
-
-    # Format
-    if ($r->is_endpoint && !$r->pattern->format) {
-        if ($path =~ /^\.([^\/]+)$/) {
-            $self->captures->{format} = $1;
-            $self->{_path} = '';
-        }
-    }
-    $self->captures->{format} ||= $r->pattern->format if $r->pattern->format;
-
-    # Update stack
-    if ($r->inline || ($r->is_endpoint && $self->_is_path_empty)) {
-        push @{$self->stack}, {%$captures};
-        delete $captures->{cb};
-        delete $captures->{app};
-    }
-
-    # Waypoint match
-    if ($r->block && $self->_is_path_empty) {
-        $self->endpoint($r);
-        return $self;
-    }
-
-    # Match children
-    my $snapshot = [@{$self->stack}];
-    for my $child (@{$r->children}) {
-
-        # Match
-        $self->match($child);
-
-        # Endpoint found
-        return $self if $self->endpoint;
-
-        # Reset path
-        $self->{_path} = $path;
-
-        # Reset stack
-        if ($r->parent) { $self->stack([@$snapshot]) }
-        else {
-            $self->captures({});
-            $self->stack([]);
-        }
-    }
-
-    $self->endpoint($r) if $r->is_endpoint && $self->_is_path_empty;
-
-    return $self;
-}
-
-sub url_for {
-    my $self     = shift;
-    my $endpoint = $self->endpoint;
-    my $values   = {};
-    my $name     = undef;
-
-    # Single argument
-    if (@_ == 1) {
-
-        # Hash
-        $values = shift if ref $_[0] eq 'HASH';
-
-        # Name
-        $name = $_[0] if $_[0];
-    }
-
-    # Multiple arguments
-    elsif (@_ > 1) {
-
-        # Odd
-        if (@_ % 2) {
-            $name   = shift;
-            $values = {@_};
-        }
-
-        # Even
-        else {
-
-            # Name and hashref
-            if (ref $_[1] eq 'HASH') {
-                $name   = shift;
-                $values = shift;
-            }
-
-            # Just values
-            else { $values = {@_} }
-
-        }
-    }
-
-    # Captures
-    my $captures = $self->captures;
-
-    # Named
-    if ($name) {
-
-        # Current route
-        if ($name eq 'current') { $name = undef }
-
-        # Find
-        else {
-            $captures = {};
-            croak qq/Route "$name" used in url_for does not exist/
-              unless $endpoint = $self->_find_route($name);
-        }
-    }
-
-    # Merge values
-    $values = {%$captures, format => undef, %$values};
-
-    # URL
-    my $url = Mojo::URL->new;
-
-    # No endpoint
-    return $url unless $endpoint;
-
-    # Base
-    $url->base($self->{_controller}->req->url->base->clone);
-    my $base = $url->base;
-    $url->base->userinfo(undef);
-
-    # Render
-    my $path = $endpoint->render($url->path->to_string, $values);
-    $url->path->parse($path);
-
-    # Fix scheme
-    if ($endpoint->is_websocket) {
-        $base->scheme(($base->scheme || '') eq 'https' ? 'wss' : 'ws');
-    }
-
-    # Fix paths
-    unshift @{$url->path->parts}, @{$base->path->parts};
-    $base->path->parts([]);
-
-    return $url;
-}
-
-sub _find_route {
-    my ($self, $name) = @_;
-
-    # Find endpoint
-    my @children = ($self->root);
-    while (my $child = shift @children) {
-
-        # Match
-        return $child if ($child->name || '') eq $name;
-
-        # Append
-        push @children, @{$child->children};
-    }
-
-    # Not found
-    return;
-}
-
-sub _is_path_empty {
-    my $self = shift;
-    return 1 if !length $self->{_path} || $self->{_path} eq '/';
-    return;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Routes::Match - Routes Visitor
-
-=head1 SYNOPSIS
-
-    use MojoX::Routes::Match;
-
-    # New match object
-    my $m = MojoX::Routes::Match->new($c);
-
-    # Match
-    $m->match($routes);
-
-=head1 DESCRIPTION
-
-L<MojoX::Routes::Match> is a visitor for L<MojoX::Routes> structures.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Routes::Match> implements the following attributes.
-
-=head2 C<captures>
-
-    my $captures = $m->captures;
-    $m           = $m->captures({foo => 'bar'});
-
-Captured parameters.
-
-=head2 C<endpoint>
-
-    my $endpoint = $m->endpoint;
-    $m           = $m->endpoint(MojoX::Routes->new);
-
-The routes endpoint that actually matched.
-
-=head2 C<root>
-
-    my $root = $m->root;
-    $m       = $m->root($routes);
-
-The root of the routes tree.
-
-=head2 C<stack>
-
-    my $stack = $m->stack;
-    $m        = $m->stack([{foo => 'bar'}]);
-
-Captured parameters with nesting history.
-
-=head1 METHODS
-
-L<MojoX::Routes::Match> inherits all methods from L<Mojo::Base> and
-implements the following ones.
-
-=head2 C<new>
-
-    my $m = MojoX::Routes::Match->new(MojoX:Controller->new);
-
-Construct a new match object.
-
-=head2 C<match>
-
-    $m->match(MojoX::Routes->new);
-
-Match against a routes tree.
-
-=head2 C<url_for>
-
-    my $url = $m->url_for;
-    my $url = $m->url_for(foo => 'bar');
-    my $url = $m->url_for({foo => 'bar'});
-    my $url = $m->url_for('named');
-    my $url = $m->url_for('named', foo => 'bar');
-    my $url = $m->url_for('named', {foo => 'bar'});
-
-Render matching route with parameters into a L<Mojo::URL> object.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,456 +0,0 @@
-package MojoX::Routes::Pattern;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use constant DEBUG => $ENV{MOJOX_ROUTES_DEBUG} || 0;
-
-__PACKAGE__->attr(defaults => sub { {} });
-__PACKAGE__->attr([qw/format pattern regex/]);
-__PACKAGE__->attr(quote_end      => ')');
-__PACKAGE__->attr(quote_start    => '(');
-__PACKAGE__->attr(relaxed_start  => '.');
-__PACKAGE__->attr(reqs           => sub { {} });
-__PACKAGE__->attr(symbol_start   => ':');
-__PACKAGE__->attr(symbols        => sub { [] });
-__PACKAGE__->attr(tree           => sub { [] });
-__PACKAGE__->attr(wildcard_start => '*');
-
-# This is the worst kind of discrimination. The kind against me!
-sub new {
-    my $self = shift->SUPER::new();
-    $self->parse(@_);
-    return $self;
-}
-
-sub match {
-    my ($self, $path) = @_;
-
-    # Match
-    my $result = $self->shape_match(\$path);
-
-    # Endpoint
-    return $result if !$path || $path eq '/';
-
-    # Partial or no match
-    return;
-}
-
-sub parse {
-    my $self    = shift;
-    my $pattern = shift;
-
-    # Shortcut
-    return $self unless defined $pattern;
-
-    # Make sure pattern starts with a slash
-    $pattern = "/$pattern" unless $pattern =~ /^\//;
-
-    # Format
-    if ($pattern =~ /\.([^\/\)]+)$/) { $self->format($1) }
-
-    # Requirements
-    my $reqs = ref $_[0] eq 'HASH' ? $_[0] : {@_};
-    $self->reqs($reqs);
-
-    # Tokenize
-    $self->pattern($pattern);
-    $self->_tokenize;
-
-    return $self;
-}
-
-sub render {
-    my ($self, $values) = @_;
-
-    # Merge values with defaults
-    $values ||= {};
-    $values = {%{$self->defaults}, %$values};
-
-    my $string   = '';
-    my $optional = 1;
-    for my $token (reverse @{$self->tree}) {
-        my $op       = $token->[0];
-        my $rendered = '';
-
-        # Slash
-        if ($op eq 'slash') {
-            $rendered = '/' unless $optional;
-        }
-
-        # Text
-        elsif ($op eq 'text') {
-            $rendered = $token->[1];
-            $optional = 0;
-        }
-
-        # Relaxed, symbol or wildcard
-        elsif ($op eq 'relaxed' || $op eq 'symbol' || $op eq 'wildcard') {
-            my $name = $token->[1];
-            $rendered = $values->{$name};
-            $rendered = '' unless defined $rendered;
-
-            my $default = $self->defaults->{$name};
-            $default = '' unless defined $default;
-
-            $optional = 0 unless $default eq $rendered;
-            $rendered = '' if $optional && $default eq $rendered;
-        }
-
-        $string = "$rendered$string";
-    }
-
-    return $string || '/';
-}
-
-sub shape_match {
-    my ($self, $pathref) = @_;
-
-    # Debug
-    if (DEBUG) {
-        my $pattern = $self->pattern || '';
-        warn "    [$$pathref] -> [$pattern]\n";
-    }
-
-    # Compile on demand
-    $self->_compile unless $self->regex;
-
-    my $regex = $self->regex;
-
-    # Debug
-    warn "    $regex\n" if DEBUG;
-
-    # Match
-    if (my @captures = $$pathref =~ /$regex/) {
-
-        # Substitute
-        $$pathref =~ s/$regex//;
-
-        # Merge captures
-        my $result = {%{$self->defaults}};
-        for my $symbol (@{$self->symbols}) {
-
-            # No captures
-            last unless @captures;
-
-            # Merge
-            my $capture = shift @captures;
-            $result->{$symbol} = $capture if defined $capture;
-        }
-        return $result;
-    }
-
-    return;
-}
-
-sub _compile {
-    my $self = shift;
-
-    my $block    = '';
-    my $regex    = '';
-    my $optional = 1;
-    for my $token (reverse @{$self->tree}) {
-        my $op       = $token->[0];
-        my $compiled = '';
-
-        # Slash
-        if ($op eq 'slash') {
-
-            # Full block
-            $block = $optional ? "(?:/$block)?" : "/$block";
-
-            $regex = "$block$regex";
-            $block = '';
-
-            next;
-        }
-
-        # Text
-        elsif ($op eq 'text') {
-            $compiled = $token->[1];
-            $optional = 0;
-        }
-
-        # Symbol
-        elsif ($op eq 'relaxed' || $op eq 'symbol' || $op eq 'wildcard') {
-            my $name = $token->[1];
-
-            unshift @{$self->symbols}, $name;
-
-            # Relaxed
-            if ($op eq 'relaxed') { $compiled = '([^\/]+)' }
-
-            # Symbol
-            elsif ($op eq 'symbol') { $compiled = '([^\/\.]+)' }
-
-            # Wildcard
-            elsif ($op eq 'wildcard') { $compiled = '(.+)' }
-
-            my $req = $self->reqs->{$name};
-            $compiled = "($req)" if $req;
-
-            $optional = 0 unless exists $self->defaults->{$name};
-
-            $compiled .= '?' if $optional;
-        }
-
-        # Add to block
-        $block = "$compiled$block";
-    }
-
-    # Not rooted with a slash
-    $regex = "$block$regex" if $block;
-
-    $regex = qr/^$regex/;
-    $self->regex($regex);
-
-    return $self;
-}
-
-sub _tokenize {
-    my $self = shift;
-
-    my $pattern        = $self->pattern;
-    my $quote_end      = $self->quote_end;
-    my $quote_start    = $self->quote_start;
-    my $relaxed_start  = $self->relaxed_start;
-    my $symbol_start   = $self->symbol_start;
-    my $wildcard_start = $self->wildcard_start;
-
-    my $tree  = [];
-    my $state = 'text';
-
-    my $quoted = 0;
-    while (length(my $char = substr $pattern, 0, 1, '')) {
-
-        # Inside a symbol
-        my $symbol = 0;
-        $symbol = 1
-          if $state eq 'relaxed'
-              || $state eq 'symbol'
-              || $state eq 'wildcard';
-
-        # Quote start
-        if ($char eq $quote_start) {
-            $quoted = 1;
-            $state  = 'symbol';
-            push @$tree, ['symbol', ''];
-            next;
-        }
-
-        # Symbol start
-        if ($char eq $symbol_start) {
-            push @$tree, ['symbol', ''] if $state ne 'symbol';
-            $state = 'symbol';
-            next;
-        }
-
-        # Relaxed start
-        if ($quoted && $char eq $relaxed_start) {
-
-            # Upgrade relaxed to wildcard
-            if ($state eq 'symbol') {
-                $state = 'relaxed';
-                $tree->[-1]->[0] = 'relaxed';
-                next;
-            }
-
-        }
-
-        # Wildcard start
-        if ($quoted && $char eq $wildcard_start) {
-
-            # Upgrade relaxed to wildcard
-            if ($state eq 'symbol') {
-                $state = 'wildcard';
-                $tree->[-1]->[0] = 'wildcard';
-                next;
-            }
-
-        }
-
-        # Quote end
-        if ($char eq $quote_end) {
-            $quoted = 0;
-            $state  = 'text';
-            next;
-        }
-
-        # Slash
-        if ($char eq '/') {
-            push @$tree, ['slash'];
-            $state = 'text';
-            next;
-        }
-
-        # Relaxed, symbol or wildcard
-        elsif ($symbol && $char =~ /\w/) {
-            $tree->[-1]->[-1] .= $char;
-            next;
-        }
-
-        # Text
-        else {
-
-            $state = 'text';
-
-            # New text element
-            unless ($tree->[-1]->[0] eq 'text') {
-                push @$tree, ['text', $char];
-                next;
-            }
-
-            # More text
-            $tree->[-1]->[-1] .= $char;
-        }
-    }
-
-    $self->tree($tree);
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Routes::Pattern - Routes Pattern
-
-=head1 SYNOPSIS
-
-    use MojoX::Routes::Pattern;
-
-    # New pattern object
-    my $pattern = MojoX::Routes::Pattern->new;
-
-=head1 DESCRIPTION
-
-L<MojoX::Routes::Pattern> is a container for routes pattern which are used to
-match paths against.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Routes::Pattern> implements the following attributes.
-
-=head2 C<defaults>
-
-    my $defaults = $pattern->defaults;
-    $pattern     = $pattern->defaults({foo => 'bar'});
-
-Default parameters.
-
-=head2 C<pattern>
-
-    my $pattern = $pattern->pattern;
-    $pattern    = $pattern->pattern('/(foo)/(bar)');
-
-Raw unparsed pattern.
-
-=head2 C<quote_end>
-
-    my $quote = $pattern->quote_end;
-    $pattern  = $pattern->quote_end(']');
-
-Character indicating the end of a quoted placeholder, defaults to C<)>.
-
-=head2 C<quote_start>
-
-    my $quote = $pattern->quote_start;
-    $pattern  = $pattern->quote_start('[');
-
-Character indicating the start of a quoted placeholder, defaults to C<(>.
-
-=head2 C<regex>
-
-    my $regex = $pattern->regex;
-    $pattern  = $pattern->regex(qr/\/foo/);
-
-Pattern in compiled regex form.
-
-=head2 C<relaxed_start>
-
-    my $relaxed = $pattern->relaxed_start;
-    $pattern    = $pattern->relaxed_start('*');
-
-Character indicating a relaxed placeholder, defaults to C<.>.
-
-=head2 C<reqs>
-
-    my $reqs = $pattern->reqs;
-    $pattern = $pattern->reqs({foo => qr/\w+/});
-
-Regex constraints.
-
-=head2 C<symbol_start>
-
-    my $symbol = $pattern->symbol_start;
-    $pattern   = $pattern->symbol_start(':');
-
-Character indicating a placeholder, defaults to C<:>.
-
-=head2 C<symbols>
-
-    my $symbols = $pattern->symbols;
-    $pattern    = $pattern->symbols(['foo', 'bar']);
-
-Placeholder names.
-
-=head2 C<tree>
-
-    my $tree = $pattern->tree;
-    $pattern = $pattern->tree([ ... ]);
-
-Pattern in parsed form.
-
-=head2 C<wildcard_start>
-
-    my $wildcard = $pattern->wildcard_start;
-    $pattern     = $pattern->wildcard_start('*');
-
-Character indicating the start of a wildcard placeholder, defaults to C<*>.
-
-=head1 METHODS
-
-L<MojoX::Routes::Pattern> inherits all methods from L<Mojo::Base> and
-implements the following ones.
-
-=head2 C<new>
-
-    my $pattern = MojoX::Routes::Pattern->new('/:controller/:action',
-        action => qr/\w+/
-    );
-
-Construct a new pattern object.
-
-=head2 C<match>
-
-    my $result = $pattern->match('/foo/bar');
-
-Match pattern against a path.
-
-=head2 C<parse>
-
-    $pattern = $pattern->parse('/:controller/:action', action => qr/\w+/);
-
-Parse a raw pattern.
-
-=head2 C<render>
-
-    my $path = $pattern->render({action => 'foo'});
-
-Render pattern into a path with parameters.
-
-=head2 C<shape_match>
-
-    my $result = $pattern->shape_match(\$path);
-
-Match pattern against a path and remove matching parts.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,510 +0,0 @@
-package MojoX::Routes;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use Mojo::URL;
-use MojoX::Routes::Pattern;
-use Scalar::Util 'weaken';
-
-__PACKAGE__->attr([qw/block inline parent partial/]);
-__PACKAGE__->attr([qw/children conditions/] => sub { [] });
-__PACKAGE__->attr(dictionary                => sub { {} });
-__PACKAGE__->attr(pattern => sub { MojoX::Routes::Pattern->new });
-
-# Yet thanks to my trusty safety sphere,
-# I sublibed with only tribial brain dablage.
-sub new {
-    my $self = shift->SUPER::new();
-
-    # Parse
-    $self->parse(@_);
-
-    # Method condition
-    $self->add_condition(
-        method => sub {
-            my ($r, $c, $captures, $methods) = @_;
-
-            # Methods
-            return unless $methods && ref $methods eq 'ARRAY';
-
-            # Match
-            my $m = lc $c->req->method;
-            $m = 'get' if $m eq 'head';
-            for my $method (@$methods) {
-                return 1 if $method eq $m;
-            }
-
-            # Nothing
-            return;
-        }
-    );
-
-    # WebSocket condition
-    $self->add_condition(
-        websocket => sub {
-            my ($r, $c, $captures) = @_;
-
-            # WebSocket
-            return 1 if $c->tx->is_websocket;
-
-            # Not a WebSocket
-            return;
-        }
-    );
-
-    return $self;
-}
-
-sub add_child {
-    my ($self, $route) = @_;
-
-    # We are the parent
-    $route->parent($self);
-    weaken $route->{parent};
-
-    # Add to tree
-    push @{$self->children}, $route;
-
-    return $self;
-}
-
-sub add_condition {
-    my ($self, $name, $condition) = @_;
-
-    # Add
-    $self->dictionary->{$name} = $condition;
-
-    return $self;
-}
-
-sub bridge { shift->route(@_)->inline(1) }
-
-sub is_endpoint {
-    my $self = shift;
-    return   if $self->inline;
-    return 1 if $self->block;
-    return   if @{$self->children};
-    return 1;
-}
-
-sub is_websocket {
-    my $self = shift;
-    return 1 if $self->{_websocket};
-    if (my $parent = $self->parent) { return $parent->is_websocket }
-    return;
-}
-
-# Dr. Zoidberg, can you note the time and declare the patient legally dead?
-# Can I! That’s my specialty!
-sub name {
-    my ($self, $name) = @_;
-
-    # New name
-    if (defined $name) {
-
-        # Generate
-        if ($name eq '*') {
-            $name = $self->pattern->pattern;
-            $name =~ s/\W+//g;
-        }
-        $self->{_name} = $name;
-
-        return $self;
-    }
-
-    return $self->{_name};
-}
-
-sub over {
-    my $self = shift;
-
-    # Shortcut
-    return $self unless @_;
-
-    # Conditions
-    my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
-    push @{$self->conditions}, @$conditions;
-
-    return $self;
-}
-
-sub parse {
-    my $self = shift;
-
-    # Pattern does the real work
-    $self->pattern->parse(@_);
-
-    return $self;
-}
-
-sub render {
-    my ($self, $path, $values) = @_;
-
-    # Path prefix
-    my $prefix = $self->pattern->render($values);
-    $path = $prefix . $path unless $prefix eq '/';
-
-    # Make sure there is always a root
-    $path = '/' if !$path && !$self->parent;
-
-    # Format
-    if ((my $format = $values->{format}) && !$self->parent) {
-        $path .= ".$format" unless $path =~ /\.[^\/]+$/;
-    }
-
-    # Parent
-    $path = $self->parent->render($path, $values) if $self->parent;
-
-    return $path;
-}
-
-# Morbo forget how you spell that letter that looks like a man wearing a hat.
-# Hello, tiny man. I will destroy you!
-sub route {
-    my $self = shift;
-
-    # New route
-    my $route = $self->new(@_);
-    $self->add_child($route);
-
-    return $route;
-}
-
-sub to {
-    my $self = shift;
-
-    # Shortcut
-    return $self unless @_;
-
-    # Single argument
-    my ($shortcut, $defaults);
-    if (@_ == 1) {
-
-        # Hash
-        $defaults = shift if ref $_[0] eq 'HASH';
-        $shortcut = shift if $_[0];
-    }
-
-    # Multiple arguments
-    else {
-
-        # Odd
-        if (@_ % 2) {
-            $shortcut = shift;
-            $defaults = {@_};
-        }
-
-        # Even
-        else {
-
-            # Shortcut and defaults
-            if (ref $_[1] eq 'HASH') {
-                $shortcut = shift;
-                $defaults = shift;
-            }
-
-            # Just defaults
-            else { $defaults = {@_} }
-        }
-    }
-
-    # Shortcut
-    if ($shortcut) {
-
-        # App
-        if (ref $shortcut || $shortcut =~ /^[\w\:]+$/) {
-            $defaults->{app} = $shortcut;
-        }
-
-        # Controller and action
-        elsif ($shortcut =~ /^([\w\-]+)?\#(\w+)?$/) {
-            $defaults->{controller} = $1 if defined $1;
-            $defaults->{action}     = $2 if defined $2;
-        }
-    }
-
-    # Pattern
-    my $pattern = $self->pattern;
-
-    # Defaults
-    my $old = $pattern->defaults;
-    $pattern->defaults({%$old, %$defaults}) if $defaults;
-
-    return $self;
-}
-
-sub to_string {
-    my $self = shift;
-    my $pattern = $self->parent ? $self->parent->to_string : '';
-    $pattern .= $self->pattern->pattern if $self->pattern->pattern;
-    return $pattern;
-}
-
-sub via {
-    my $self = shift;
-
-    # Methods
-    my $methods = ref $_[0] ? $_[0] : [@_];
-
-    # Shortcut
-    return $self unless @$methods;
-
-    # Condition
-    push @{$self->conditions}, method => [map { lc $_ } @$methods];
-
-    return $self;
-}
-
-sub waypoint { shift->route(@_)->block(1) }
-
-sub websocket {
-    my $self = shift;
-
-    # Condition
-    push @{$self->conditions}, websocket => 1;
-    $self->{_websocket} = 1;
-
-    return $self;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Routes - Always Find Your Destination With Routes
-
-=head1 SYNOPSIS
-
-    use MojoX::Routes;
-
-    # New routes tree
-    my $r = MojoX::Routes->new;
-
-    # Normal route matching "/articles" with parameters "controller" and
-    # "action"
-    $r->route('/articles')->to(controller => 'article', action => 'list');
-
-    # Route with a placeholder matching everything but "/" and "."
-    $r->route('/:controller')->to(action => 'list');
-
-    # Route with a placeholder and regex constraint
-    $r->route('/articles/:id', id => qr/\d+/)
-      ->to(controller => 'article', action => 'view');
-
-    # Route with an optional parameter "year"
-    $r->route('/archive/:year')
-      ->to(controller => 'archive', action => 'list', year => undef);
-
-    # Nested route for two actions sharing the same "controller" parameter
-    my $books = $r->route('/books/:id')->to(controller => 'book');
-    $books->route('/edit')->to(action => 'edit');
-    $books->route('/delete')->to(action => 'delete');
-
-    # Bridges can be used to chain multiple routes
-    $r->bridge->to(controller => 'foo', action =>'auth')
-      ->route('/blog')->to(action => 'list');
-
-    # Waypoints are similar to bridges and nested routes but can also match
-    # if they are not the actual endpoint of the whole route
-    my $b = $r->waypoint('/books')->to(controller => 'books', action => 'list');
-    $b->route('/:id', id => qr/\d+/)->to(action => 'view');
-
-=head1 DESCRIPTION
-
-L<MojoX::Routes> is a very powerful implementation of the famous routes
-pattern and the core of the L<Mojolicious> web framework.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Routes> implements the following attributes.
-
-=head2 C<block>
-
-    my $block = $r->block;
-    $r        = $r->block(1);
-
-Allow this route to match even if it's not an endpoint, used for waypoints.
-
-=head2 C<children>
-
-    my $children = $r->children;
-    $r           = $r->children([MojoX::Routes->new]);
-
-The children of this routes object, used for nesting routes.
-
-=head2 C<conditions>
-
-    my $conditions  = $r->conditions;
-    $r              = $r->conditions([foo => qr/\w+/]);
-
-Contains condition parameters for this route, used for C<over>.
-
-=head2 C<dictionary>
-
-    my $dictionary = $r->dictionary;
-    $r             = $r->dictionary({foo => sub { ... }});
-
-Contains all available conditions for this route.
-There are currently two conditions built in, C<method> and C<websocket>.
-
-=head2 C<inline>
-
-    my $inline = $r->inline;
-    $r         = $r->inline(1);
-
-Allow C<bridge> semantics for this route.
-
-=head2 C<parent>
-
-    my $parent = $r->parent;
-    $r         = $r->parent(MojoX::Routes->new);
-
-The parent of this route, used for nesting routes.
-
-=head2 C<partial>
-
-    my $partial = $r->partial;
-    $r          = $r->partial('path');
-
-Route has no specific end, remaining characters will be captured with the
-partial name.
-Note that this attribute is EXPERIMENTAL and might change without warning!
-
-=head2 C<pattern>
-
-    my $pattern = $r->pattern;
-    $r          = $r->pattern(MojoX::Routes::Pattern->new);
-
-Pattern for this route, by default a L<MojoX::Routes::Pattern> object and
-used for matching.
-
-=head1 METHODS
-
-L<MojoX::Routes> inherits all methods from L<Mojo::Base> and implements the
-following ones.
-
-=head2 C<new>
-
-    my $r = MojoX::Routes->new;
-    my $r = MojoX::Routes->new('/:controller/:action');
-
-Construct a new route object.
-
-=head2 C<add_child>
-
-    $r = $r->add_child(MojoX::Route->new);
-
-Add a new child to this route.
-
-=head2 C<add_condition>
-
-    $r = $r->add_condition(foo => sub { ... });
-
-Add a new condition for this route.
-
-=head2 C<bridge>
-
-    my $bridge = $r->bridge;
-    my $bridge = $r->bridge('/:controller/:action');
-
-Add a new bridge to this route as a nested child.
-
-=head2 C<is_endpoint>
-
-    my $is_endpoint = $r->is_endpoint;
-
-Returns true if this route qualifies as an endpoint.
-
-=head2 C<is_websocket>
-
-    my $is_websocket = $r->is_websocket;
-
-Returns true if this route leads to a WebSocket.
-
-=head2 C<name>
-
-    my $name = $r->name;
-    $r       = $r->name('foo');
-    $r       = $r->name('*');
-
-The name of this route, the special value C<*> will generate a name based on
-the route pattern.
-Note that the name C<current> is reserved for refering to the current route.
-
-=head2 C<over>
-
-    $r = $r->over(foo => qr/\w+/);
-
-Apply condition parameters to this route.
-
-=head2 C<parse>
-
-    $r = $r->parse('/:controller/:action');
-
-Parse a pattern.
-
-=head2 C<render>
-
-    my $path = $r->render($path);
-    my $path = $r->render($path, {foo => 'bar'});
-
-Render route with parameters into a path.
-
-=head2 C<route>
-
-    my $route = $r->route('/:c/:a', a => qr/\w+/);
-
-Add a new nested child to this route.
-
-=head2 C<to>
-
-    my $to  = $r->to;
-    $r = $r->to(action => 'foo');
-    $r = $r->to({action => 'foo'});
-    $r = $r->to('controller#action');
-    $r = $r->to('controller#action', foo => 'bar');
-    $r = $r->to('controller#action', {foo => 'bar'});
-    $r = $r->to($app);
-    $r = $r->to($app, foo => 'bar');
-    $r = $r->to($app, {foo => 'bar'});
-    $r = $r->to('MyApp');
-    $r = $r->to('MyApp', foo => 'bar');
-    $r = $r->to('MyApp', {foo => 'bar'});
-
-Set default parameters for this route.
-
-=head2 C<to_string>
-
-    my $string = $r->to_string;
-
-Stringifies the whole route.
-
-=head2 C<via>
-
-    $r = $r->via('get');
-    $r = $r->via(qw/get post/);
-    $r = $r->via([qw/get post/]);
-
-Apply C<method> constraint to this route.
-
-=head2 C<waypoint>
-
-    my $route = $r->waypoint('/:c/:a', a => qr/\w+/);
-
-Add a waypoint to this route as nested child.
-
-=head2 C<websocket>
-
-    $route->websocket;
-
-Apply C<websocket> constraint to this route.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,254 +0,0 @@
-package MojoX::Session::Cookie::Controller;
-
-use strict;
-use warnings;
-
-use base 'MojoX::Controller';
-
-use Mojo::Cookie::Response;
-use Mojo::Transaction::HTTP;
-use Mojo::Util 'hmac_md5_sum';
-
-__PACKAGE__->attr(tx => sub { Mojo::Transaction::HTTP->new });
-
-# For the last time, I don't like lilacs!
-# Your first wife was the one who liked lilacs!
-# She also liked to shut up!
-sub cookie {
-    my ($self, $name, $value, $options) = @_;
-
-    # Shortcut
-    return unless $name;
-
-    # Response cookie
-    if (defined $value) {
-
-        # Cookie too big
-        $self->app->log->error(qq/Cookie "$name" is bigger than 4096 bytes./)
-          if length $value > 4096;
-
-        # Create new cookie
-        $options ||= {};
-        my $cookie = Mojo::Cookie::Response->new(
-            name  => $name,
-            value => $value,
-            %$options
-        );
-        $self->res->cookies($cookie);
-        return $self;
-    }
-
-    # Request cookie
-    unless (wantarray) {
-        return unless my $cookie = $self->req->cookie($name);
-        return $cookie->value;
-    }
-
-    # Request cookies
-    my @cookies = $self->req->cookie($name);
-    return map { $_->value } @cookies;
-}
-
-# You two make me ashamed to call myself an idiot.
-sub flash {
-    my $self = shift;
-
-    # Get
-    my $session = $self->stash->{'mojo.session'};
-    if ($_[0] && !defined $_[1] && !ref $_[0]) {
-        return unless $session && ref $session eq 'HASH';
-        return unless my $flash = $session->{old_flash};
-        return unless ref $flash eq 'HASH';
-        return $flash->{$_[0]};
-    }
-
-    # Initialize
-    $session = $self->session;
-    my $flash = $session->{flash};
-    $flash = {} unless $flash && ref $flash eq 'HASH';
-    $session->{flash} = $flash;
-
-    # Hash
-    return $flash unless @_;
-
-    # Set
-    my $values = exists $_[1] ? {@_} : $_[0];
-    $session->{flash} = {%$flash, %$values};
-
-    return $self;
-}
-
-sub req { shift->tx->req }
-sub res { shift->tx->res }
-
-# Why am I sticky and naked? Did I miss something fun?
-sub session {
-    my $self = shift;
-
-    # Get
-    my $stash   = $self->stash;
-    my $session = $stash->{'mojo.session'};
-    if ($_[0] && !defined $_[1] && !ref $_[0]) {
-        return unless $session && ref $session eq 'HASH';
-        return $session->{$_[0]};
-    }
-
-    # Initialize
-    $session = {} unless $session && ref $session eq 'HASH';
-    $stash->{'mojo.session'} = $session;
-
-    # Hash
-    return $session unless @_;
-
-    # Set
-    my $values = exists $_[1] ? {@_} : $_[0];
-    $stash->{'mojo.session'} = {%$session, %$values};
-
-    return $self;
-}
-
-sub signed_cookie {
-    my ($self, $name, $value, $options) = @_;
-
-    # Shortcut
-    return unless $name;
-
-    # Secret
-    my $secret = $self->app->secret;
-
-    # Response cookie
-    if (defined $value) {
-
-        # Sign value
-        my $signature = hmac_md5_sum $value, $secret;
-        $value = $value .= "--$signature";
-
-        # Create cookie
-        my $cookie = $self->cookie($name, $value, $options);
-        return $cookie;
-    }
-
-    # Request cookies
-    my @values = $self->cookie($name);
-    my @results;
-    for my $value (@values) {
-
-        # Check signature
-        if ($value =~ s/\-\-([^\-]+)$//) {
-            my $signature = $1;
-            my $check = hmac_md5_sum $value, $secret;
-
-            # Verified
-            if ($signature eq $check) { push @results, $value }
-
-            # Bad cookie
-            else {
-                $self->app->log->debug(
-                    qq/Bad signed cookie "$name", possible hacking attempt./);
-            }
-        }
-
-        # Not signed
-        else { $self->app->log->debug(qq/Cookie "$name" not signed./) }
-    }
-
-    return wantarray ? @results : $results[0];
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Session::Cookie::Controller - Controller Base Class
-
-=head1 SYNOPSIS
-
-    use base 'MojoX::Session::Cookie::Controller';
-
-=head1 DESCRIPTION
-
-L<MojoX::Session::Cookie::Controller> is a controller base class.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Session::Cookie::Controller> inherits all attributes from
-L<MojoX::Controller> and implements the following new ones.
-
-=head2 C<tx>
-
-    my $tx = $c->tx;
-
-The transaction that is currently being processed, defaults to a
-L<Mojo::Transaction::HTTP> object.
-
-=head1 METHODS
-
-L<MojoX::Session::Cookie::Controller> inherits all methods from
-L<MojoX::Controller> and implements the following ones.
-
-=head2 C<cookie>
-
-    $c         = $c->cookie(foo => 'bar');
-    $c         = $c->cookie(foo => 'bar', {path => '/'});
-    my $value  = $c->cookie('foo');
-    my @values = $c->cookie('foo');
-
-Access request cookie values and create new response cookies.
-
-=head2 C<flash>
-
-    my $flash = $c->flash;
-    my $foo   = $c->flash('foo');
-    $c        = $c->flash({foo => 'bar'});
-    $c        = $c->flash(foo => 'bar');
-
-Data storage persistent for the next request, stored in the session.
-
-    $c->flash->{foo} = 'bar';
-    my $foo = $c->flash->{foo};
-    delete $c->flash->{foo};
-
-=head2 C<req>
-
-    my $req = $c->req;
-
-Alias for C<$c->tx->req>.
-Usually refers to a L<Mojo::Message::Request> object.
-
-=head2 C<res>
-
-    my $res = $c->res;
-
-Alias for C<$c->tx->res>.
-Usually refers to a L<Mojo::Message::Response> object.
-
-=head2 C<session>
-
-    my $session = $c->session;
-    my $foo     = $c->session('foo');
-    $c          = $c->session({foo => 'bar'});
-    $c          = $c->session(foo => 'bar');
-
-Persistent data storage, by default stored in a signed cookie.
-Note that cookies are generally limited to 4096 bytes of data.
-
-    $c->session->{foo} = 'bar';
-    my $foo = $c->session->{foo};
-    delete $c->session->{foo};
-
-=head2 C<signed_cookie>
-
-    $c         = $c->signed_cookie(foo => 'bar');
-    $c         = $c->signed_cookie(foo => 'bar', {path => '/'});
-    my $value  = $c->signed_cookie('foo');
-    my @values = $c->signed_cookie('foo');
-
-Access signed request cookie values and create new signed response cookies.
-Cookies failing signature verification will be automatically discarded.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,163 +0,0 @@
-package MojoX::Session::Cookie;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-use Mojo::Util qw/b64_decode b64_encode/;
-use Storable qw/freeze thaw/;
-
-__PACKAGE__->attr('cookie_domain');
-__PACKAGE__->attr(cookie_name        => 'mojolicious');
-__PACKAGE__->attr(cookie_path        => '/');
-__PACKAGE__->attr(default_expiration => 3600);
-
-# Bender, quit destroying the universe!
-sub load {
-    my ($self, $c) = @_;
-
-    # Session cookie
-    return unless my $value = $c->signed_cookie($self->cookie_name);
-
-    # Decode
-    b64_decode $value;
-
-    # Thaw
-    my $session = thaw $value;
-
-    # Expiration
-    return unless my $expires = delete $session->{expires};
-    return unless $expires > time;
-
-    # Content
-    my $stash = $c->stash;
-    return unless $stash->{'mojo.active_session'} = keys %$session;
-    $stash->{'mojo.session'} = $session;
-
-    # Flash
-    $session->{old_flash} = delete $session->{flash} if $session->{flash};
-}
-
-# Emotions are dumb and should be hated.
-sub store {
-    my ($self, $c) = @_;
-
-    # Session
-    my $stash = $c->stash;
-    return unless my $session = $stash->{'mojo.session'};
-    return unless keys %$session || $stash->{'mojo.active_session'};
-
-    # Flash
-    delete $session->{old_flash};
-    delete $session->{flash} unless keys %{$session->{flash}};
-
-    # Default to expiring session
-    my $expires = 1;
-    my $value   = '';
-
-    # Actual session data
-    my $default = delete $session->{expires};
-    if (keys %$session) {
-
-        # Expiration
-        $expires = $session->{expires} = $default
-          ||= time + $self->default_expiration;
-
-        # Freeze
-        $value = freeze $session;
-
-        # Encode
-        b64_encode $value, '';
-    }
-
-    # Options
-    my $options = {expires => $expires, path => $self->cookie_path};
-    my $domain = $self->cookie_domain;
-    $options->{domain} = $domain if $domain;
-
-    # Session cookie
-    $c->signed_cookie($self->cookie_name, $value, $options);
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Session::Cookie - Signed Cookie Based Sessions
-
-=head1 SYNOPSIS
-
-    use MojoX::Session::Cookie;
-    use MojoX::Session::Cookie::Controller;
-
-    my $session = MojoX::Session::Cookie->new;
-    my $c = MojoX::Session::Cookie::Controller->new;
-    $session->load($c);
-    $c->session(foo => 'bar');
-    $session->store($c);
-
-=head1 DESCRIPTION
-
-L<MojoX::Session::Cookie> is a very simple signed cookie based session
-implementation.
-All data gets stored on the client side, but is protected from unwanted
-changes with a signature.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Session::Cookie> implements the following attributes.
-
-=head2 C<cookie_domain>
-
-    my $domain = $session->cookie_domain;
-    $session   = $session->cookie_domain('.example.com');
-
-Domain for session cookie, not defined by default.
-
-=head2 C<cookie_name>
-
-    my $name = $session->cookie_name;
-    $session = $session->cookie_name('session');
-
-Name of the signed cookie used to store session data, defaults to
-C<mojolicious>.
-
-=head2 C<cookie_path>
-
-    my $path = $session->cookie_path;
-    $session = $session->cookie_path('/foo');
-
-Path for session cookie, defaults to C</>.
-
-=head2 C<default_expiration>
-
-    my $time = $session->default_expiration;
-    $session = $session->default_expiration(3600);
-
-Time for the session to expire in seconds from now, defaults to C<3600>.
-The expiration timeout gets refreshed for every request.
-
-=head1 METHODS
-
-L<MojoX::Session::Cookie> inherits all methods from L<Mojo::Base> and
-implements the following ones.
-
-=head2 C<load>
-
-    $session->load($c);
-
-Load session data from signed cookie.
-
-=head2 C<store>
-
-    $session->store($c);
-
-Store session data in signed cookie.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -1,101 +0,0 @@
-package MojoX::Types;
-
-use strict;
-use warnings;
-
-use base 'Mojo::Base';
-
-# Once again, the conservative, sandwich-heavy portfolio pays off for the
-# hungry investor.
-__PACKAGE__->attr(
-    types => sub {
-        return {
-            atom => 'application/atom+xml',
-            bin  => 'application/octet-stream',
-            css  => 'text/css',
-            gif  => 'image/gif',
-            gz   => 'application/gzip',
-            htm  => 'text/html',
-            html => 'text/html',
-            ico  => 'image/x-icon',
-            jpeg => 'image/jpeg',
-            jpg  => 'image/jpeg',
-            js   => 'application/x-javascript',
-            json => 'application/json',
-            mp3  => 'audio/mpeg',
-            png  => 'image/png',
-            rss  => 'application/rss+xml',
-            svg  => 'image/svg+xml',
-            tar  => 'application/x-tar',
-            txt  => 'text/plain',
-            xml  => 'text/xml',
-            zip  => 'application/zip'
-        };
-    }
-);
-
-# Magic. Got it.
-sub type {
-    my ($self, $ext, $type) = @_;
-
-    # Set
-    if ($type) {
-        $self->types->{$ext} = $type;
-        return $self;
-    }
-
-    return $self->types->{$ext || ''};
-}
-
-1;
-__END__
-
-=head1 NAME
-
-MojoX::Types - MIME Types
-
-=head1 SYNOPSIS
-
-    use MojoX::Types;
-
-    # New type list
-    my $types = MojoX::Types->new;
-
-    # Get MIME type for ".png"
-    my $type = $types->type('png');
-
-    # Add MIME type for ".foo"
-    $types->type(foo => 'mojo/foo');
-
-=head1 DESCRIPTION
-
-L<MojoX::Types> is a container for MIME types.
-
-=head1 ATTRIBUTES
-
-L<MojoX::Types> implements the following attributes.
-
-=head2 C<types>
-
-    my $map = $types->types;
-    $types  = $types->types({png => 'image/png'});
-
-List of MIME types.
-
-=head1 METHODS
-
-L<MojoX::Types> inherits all methods from L<Mojo::Base> and implements the
-following ones.
-
-=head2 C<type>
-
-    my $type = $types->type('png');
-    $types   = $types->type(png => 'image/png');
-
-Get or set MIME type for file extension.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
-
-=cut
@@ -0,0 +1,89 @@
+package Mojolicious::Command::Cgi;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Server::CGI;
+
+use Getopt::Long 'GetOptions';
+
+__PACKAGE__->attr(description => <<'EOF');
+Start application with CGI.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 cgi [OPTIONS]
+
+These options are available:
+  --nph   Enable non-parsed-header mode.
+EOF
+
+# Hi, Super Nintendo Chalmers!
+sub run {
+    my $self = shift;
+    my $cgi  = Mojo::Server::CGI->new;
+
+    # Options
+    local @ARGV = @_ if @_;
+    GetOptions(nph => sub { $cgi->nph(1) });
+
+    # Run
+    $cgi->run;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Cgi - CGI Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::CGI;
+
+    my $cgi = Mojolicious::Command::CGI->new;
+    $cgi->run(@ARGV);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Cgi> is a command interface to L<Mojo::Server::CGI>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Cgi> inherits all attributes from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $cgi->description;
+    $cgi            = $cgi->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $cgi->usage;
+    $cgi      = $cgi->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Cgi> inherits all methods from L<Mojo::Command> and
+implements the following new ones.
+
+=head2 C<run>
+
+    $cgi = $cgi->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,117 @@
+package Mojolicious::Command::Daemon;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Server::Daemon;
+
+use Getopt::Long 'GetOptions';
+
+__PACKAGE__->attr(description => <<'EOF');
+Start application with HTTP 1.1 and WebSocket server.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 daemon [OPTIONS]
+
+These options are available:
+  --clients <number>      Set maximum number of concurrent clients, defaults
+                          to 1000.
+  --group <name>          Set group name for process.
+  --keepalive <seconds>   Set keep-alive timeout, defaults to 15.
+  --listen <locations>    Set a comma separated list of locations you want to
+                          listen on, defaults to http://*:3000.
+  --queue <size>          Set listen queue size, defaults to SOMAXCONN.
+  --reload                Automatically reload application when the source
+                          code changes.
+  --requests <number>     Set maximum number of requests per keep-alive
+                          connection, defaults to 100.
+  --reverseproxy          Activate reverse proxy support, defaults to the
+                          value of MOJO_REVERSE_PROXY.
+  --user <name>           Set user name for process.
+  --websocket <seconds>   Set WebSocket timeout, defaults to 300.
+EOF
+
+
+# This is the worst thing you've ever done.
+# You say that so often that it lost its meaning.
+sub run {
+    my $self   = shift;
+    my $daemon = Mojo::Server::Daemon->new;
+
+    # Options
+    local @ARGV = @_ if @_;
+    GetOptions(
+        'clients=i'    => sub { $daemon->max_clients($_[1]) },
+        'group=s'      => sub { $daemon->group($_[1]) },
+        'keepalive=i'  => sub { $daemon->keep_alive_timeout($_[1]) },
+        'listen=s'     => sub { $daemon->listen($_[1]) },
+        'queue=i'      => sub { $daemon->listen_queue_size($_[1]) },
+        reload         => sub { $ENV{MOJO_RELOAD} = 1 },
+        'requests=i'   => sub { $daemon->max_requests($_[1]) },
+        'reverseproxy' => sub { $ENV{MOJO_REVERSE_PROXY} = 1 },
+        'user=s'       => sub { $daemon->user($_[1]) },
+        'websocket=i'  => sub { $daemon->websocket_timeout($_[1]) }
+    );
+
+    # Run
+    $daemon->run;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Daemon - Daemon Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Daemon;
+
+    my $daemon = Mojolicious::Command::Daemon->new;
+    $daemon->run(@ARGV);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Daemon> is a command interface to
+L<Mojo::Server::Daemon>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Daemon> inherits all attributes from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $daemon->description;
+    $daemon         = $daemon->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $daemon->usage;
+    $daemon   = $daemon->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Daemon> inherits all methods from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<run>
+
+    $daemon = $daemon->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,81 @@
+package Mojolicious::Command::Fastcgi;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Server::FastCGI;
+
+__PACKAGE__->attr(description => <<'EOF');
+Start application with FastCGI.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 fastcgi
+EOF
+
+# Oh boy! Sleep! That's when I'm a Viking!
+sub run {
+    my $self    = shift;
+    my $fastcgi = Mojo::Server::FastCGI->new;
+
+    # Run
+    $fastcgi->run;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Fastcgi - FastCGI Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Fastcgi;
+
+    my $fastcgi = Mojolicious::Command::Fastcgi->new;
+    $fastcgi->run;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Fastcgi> is a command interface to
+L<Mojo::Server::FastCGI>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::FastCGI> inherits all attributes from
+L<Mojo::Command> and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $fastcgi->description;
+    $fastcgi        = $fastcgi->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $fastcgi->usage;
+    $fastcgi  = $fastcgi->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Fastcgi> inherits all methods from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<run>
+
+    $fastcgi = $fastcgi->run;
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -3,7 +3,7 @@ package Mojolicious::Command::Generate;
 use strict;
 use warnings;
 
-use base 'Mojo::Commands';
+use base 'Mojolicious::Commands';
 
 __PACKAGE__->attr(description => <<'EOF');
 Generate files and directories from templates.
@@ -46,7 +46,7 @@ L<Mojolicious::Command::Generate> lists available generators.
 =head1 ATTRIBUTES
 
 L<Mojolicious::Command::Generate> inherits all attributes from
-L<Mojo::Commands> and implements the following new ones.
+L<Mojolicious::Commands> and implements the following new ones.
 
 =head2 C<description>
 
@@ -80,7 +80,7 @@ L<Mojo::Command::Generate> and L<Mojolicious::Command::Generate>.
 =head1 METHODS
 
 L<Mojolicious::Command::Generate> inherits all methods from
-L<Mojo::Commands>.
+L<Mojolicious::Commands>.
 
 =head1 SEE ALSO
 
@@ -0,0 +1,126 @@
+package Mojolicious::Command::Get;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Client;
+use Mojo::IOLoop;
+use Mojo::Transaction::HTTP;
+use Mojo::Util 'decode';
+
+use Getopt::Long 'GetOptions';
+
+__PACKAGE__->attr(description => <<'EOF');
+Get file from URL.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 get [OPTIONS] [URL]
+
+These options are available:
+  --verbose   Print response start line and headers to STDERR.
+EOF
+
+# I hope this has taught you kids a lesson: kids never learn.
+sub run {
+    my $self = shift;
+
+    # Options
+    local @ARGV = @_ if @_;
+    my $verbose = 0;
+    GetOptions('verbose' => sub { $verbose = 1 });
+
+    # URL
+    my $url = $ARGV[0];
+    die $self->usage unless $url;
+    decode 'UTF-8', $url;
+
+    # Client
+    my $client = Mojo::Client->new(ioloop => Mojo::IOLoop->singleton);
+
+    # Silence
+    $client->log->level('fatal');
+
+    # Application
+    $client->app($ENV{MOJO_APP} || 'Mojo::HelloWorld')
+      unless $url =~ /^\w+:\/\//;
+
+    # Transaction
+    my $tx = $client->build_tx(GET => $url);
+    my $chunks = 0;
+    $tx->res->body(
+        sub {
+            my ($res, $chunk) = @_;
+            print STDERR $tx->res->build_start_line if $verbose;
+            print STDERR $res->headers->to_string, "\n\n" if $verbose;
+            print $chunk;
+            $verbose = 0;
+            $chunks++;
+        }
+    );
+
+    # Request
+    $client->start($tx);
+
+    # Error
+    my ($message, $code) = $tx->error;
+    print qq/Couldn't open page "$url". ($message)\n/
+      if $message && !$code && !$chunks;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Get - Get Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Get;
+
+    my $get = Mojolicious::Command::Get->new;
+    $get->run(@ARGV);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Get> is a command interface to L<Mojo::Client>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Get> inherits all attributes from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $get->description;
+    $get            = $get->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $get->usage;
+    $get      = $get->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Get> inherits all methods from L<Mojo::Command> and
+implements the following new ones.
+
+=head2 C<run>
+
+    $get = $get->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -44,9 +44,11 @@ sub run {
     # Generate
     my $all = $self->get_all_data($class);
     for my $file (keys %$all) {
-        my $prefix = $file =~ /\.\w+\.\w+$/ ? $templates : $public;
-        my $path = $self->rel_file("$prefix/$file");
-        $self->write_file($path, $all->{$file});
+        my $prefix  = $file =~ /\.\w+\.\w+$/ ? $templates : $public;
+        my $path    = $self->rel_file("$prefix/$file");
+        my $content = $all->{$file};
+        utf8::encode $content if utf8::is_utf8 $content;
+        $self->write_file($path, $content);
     }
 
     return $self;
@@ -0,0 +1,85 @@
+package Mojolicious::Command::Psgi;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Server::PSGI;
+
+# Don't let Krusty's death get you down, boy.
+# People die all the time, just like that.
+# Why, you could wake up dead tomorrow! Well, good night.
+__PACKAGE__->attr(description => <<'EOF');
+Start application with PSGI.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 psgi
+EOF
+
+# D’oh.
+sub run {
+    my $self = shift;
+    my $psgi = Mojo::Server::PSGI->new;
+
+    # Preload
+    $psgi->app;
+
+    # Return app callback
+    return sub { $psgi->run(@_) };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Psgi - PSGI Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Psgi;
+
+    my $psgi = Mojolicious::Command::Psgi->new;
+    my $app = $psgi->run;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Psgi> is a command interface to
+L<Mojo::Server::PSGI>.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Psgi> inherits all attributes from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $psgi->description;
+    $psgi           = $psgi->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $psgi->usage;
+    $psgi     = $psgi->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Psgi> inherits all methods from L<Mojo::Command> and
+implements the following new ones.
+
+=head2 C<run>
+
+    my $app = $psgi->run;
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,122 @@
+package Mojolicious::Command::Test;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Cwd;
+use FindBin;
+use File::Spec;
+use Test::Harness;
+
+# Okay folks, show's over. Nothing to see here, show's... Oh my god!
+# A horrible plane crash! Hey everybody, get a load of this flaming wreckage!
+# Come on, crowd around, crowd around!
+__PACKAGE__->attr(description => <<'EOF');
+Run unit tests.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 test [TESTS]
+EOF
+
+# My eyes! The goggles do nothing!
+sub run {
+    my ($self, @tests) = @_;
+
+    # Search tests
+    unless (@tests) {
+        my @base = File::Spec->splitdir(File::Spec->abs2rel($FindBin::Bin));
+
+        # Test directory in the same directory as "mojo" (t)
+        my $path = File::Spec->catdir(@base, 't');
+
+        # Test dirctory in the directory above "mojo" (../t)
+        $path = File::Spec->catdir(@base, '..', 't') unless -d $path;
+        unless (-d $path) {
+            print "Can't find test directory.\n";
+            return;
+        }
+
+        # List test files
+        my @dirs = ($path);
+        while (my $dir = shift @dirs) {
+            opendir(my $fh, $dir);
+            for my $file (readdir($fh)) {
+                next if $file eq '.';
+                next if $file eq '..';
+                my $fpath = File::Spec->catfile($dir, $file);
+                push @dirs, File::Spec->catdir($dir, $file) if -d $fpath;
+                push @tests,
+                  File::Spec->abs2rel(
+                    Cwd::realpath(
+                        File::Spec->catfile(File::Spec->splitdir($fpath))
+                    )
+                  ) if (-f $fpath) && ($fpath =~ /\.t$/);
+            }
+            closedir $fh;
+        }
+
+        $path = Cwd::realpath($path);
+        print "Running tests from '$path'.\n";
+    }
+
+    # Run tests
+    runtests(@tests);
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Test - Test Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Test;
+
+    my $test = Mojolicious::Command::Test->new;
+    $test->run(@ARGV);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Test> is a test script.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Test> inherits all attributes from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $test->description;
+    $test           = $test->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $test->usage;
+    $test     = $test->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Test> inherits all methods from L<Mojo::Command> and
+implements the following new ones.
+
+=head2 C<run>
+
+    $test = $test->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,130 @@
+package Mojolicious::Command::Version;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Command';
+
+use Mojo::Client;
+use Mojo::IOLoop;
+use Mojo::Server::Daemon;
+use Mojolicious;
+
+__PACKAGE__->attr(description => <<'EOF');
+Show versions of installed modules.
+EOF
+__PACKAGE__->attr(usage => <<"EOF");
+usage: $0 version
+
+EOF
+
+# If at first you don't succeed, give up.
+sub run {
+    my $self = shift;
+
+    # Mojo
+    my $mojo     = $Mojolicious::VERSION;
+    my $codename = $Mojolicious::CODENAME;
+
+    # Latest version
+    my $latest = $mojo;
+    eval {
+        Mojo::Client->new->max_redirects(3)
+          ->get('search.cpan.org/dist/Mojolicious')->res->dom('.version')
+          ->each(sub { $latest = $_->text if $_->text =~ /^[\d\.]+$/ });
+    };
+
+    # Message
+    my $message = 'This version is up to date, have fun!';
+    $message = 'Thanks for testing a development release, you are awesome!'
+      if $latest < $mojo;
+    $message = "You might want to update your Mojolicious to $latest."
+      if $latest > $mojo;
+
+    # Epoll
+    my $epoll = Mojo::IOLoop::EPOLL() ? $IO::Epoll::VERSION : 'not installed';
+
+    # KQueue
+    my $kqueue =
+      Mojo::IOLoop::KQUEUE() ? $IO::KQueue::VERSION : 'not installed';
+
+    # TLS
+    my $tls =
+      Mojo::IOLoop::TLS() ? $IO::Socket::SSL::VERSION : 'not installed';
+
+    # Bonjour
+    my $bonjour =
+      eval 'Mojo::Server::Daemon::BONJOUR()'
+      ? $Net::Rendezvous::Publish::VERSION
+      : 'not installed';
+
+    print <<"EOF";
+CORE
+  Perl        ($], $^O)
+  Mojolicious ($mojo, $codename)
+
+OPTIONAL
+  IO::Epoll                ($epoll)
+  IO::KQueue               ($kqueue)
+  IO::Socket::SSL          ($tls)
+  Net::Rendezvous::Publish ($bonjour)
+
+$message
+EOF
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Command::Version - Version Command
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Command::Version;
+
+    my $v = Mojolicious::Command::Version->new;
+    $v->run(@ARGV);
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::Version> shows versions of installed modules.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::Version> inherits all attributes from
+L<Mojo::Command> and implements the following new ones.
+
+=head2 C<description>
+
+    my $description = $v->description;
+    $v              = $v->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 C<usage>
+
+    my $usage = $v->usage;
+    $v        = $v->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::Version> inherits all methods from L<Mojo::Command>
+and implements the following new ones.
+
+=head2 C<run>
+
+    $get = $v->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -3,12 +3,12 @@ package Mojolicious::Commands;
 use strict;
 use warnings;
 
-use base 'Mojo::Commands';
+use base 'Mojo::Command';
 
-# One day a man has everything, the next day he blows up a $400 billion
-# space station, and the next day he has nothing. It makes you think.
 use Getopt::Long qw/GetOptions :config pass_through/;
 
+# One day a man has everything, the next day he blows up a $400 billion
+# space station, and the next day he has nothing. It makes you think.
 __PACKAGE__->attr(hint => <<"EOF");
 
 These options are available for all commands:
@@ -27,7 +27,7 @@ BEGIN {
     GetOptions(
         'home=s' => sub { $ENV{MOJO_HOME} = $_[1] },
         'mode=s' => sub { $ENV{MOJO_MODE} = $_[1] }
-    ) unless Mojo::Commands->detect;
+    ) unless Mojo::Command->detect;
 }
 
 1;
@@ -39,7 +39,7 @@ Mojolicious::Commands - Commands
 
 =head1 SYNOPSIS
 
-    use Mojo::Commands;
+    use Mojolicious::Commands;
 
     # Command line interface
     my $commands = Mojolicious::Commands->new;
@@ -52,11 +52,42 @@ L<Mojolicious> framework.
 It will automatically detect available commands in the
 L<Mojolicious::Command> namespace.
 
-These commands are available by default in addition to the commands listed in
-L<Mojo::Commands>.
+These commands are available by default.
 
 =over 4
 
+=item C<help>
+
+    mojo
+    mojo help
+
+List available commands with short descriptions.
+
+    mojo help <command>
+
+List available options for the command with short descriptions.
+
+=item C<cgi>
+
+    mojo cgi
+    script/myapp cgi
+
+Start application with CGI backend.
+
+=item C<daemon>
+
+    mojo cgi
+    script/myapp daemon
+
+Start application with standalone HTTP 1.1 server backend.
+
+=item C<fastcgi>
+
+    mojo fastcgi
+    script/myapp fastcgi
+
+Start application with FastCGI backend.
+
 =item C<generate>
 
     mojo generate
@@ -87,6 +118,13 @@ Generate a fully functional L<Mojolicious::Lite> application.
 
 Generate C<Makefile.PL> file for application.
 
+=item C<get>
+
+   mojo get http://mojolicious.org
+   script/myapp get /foo
+
+Perform GET request to remote host or local application.
+
 =item C<inflate>
 
     myapp.pl inflate
@@ -100,11 +138,26 @@ Turn embedded files from the C<DATA> section into real files.
 
 List application routes.
 
+=item C<test>
+
+   mojo test
+   script/myapp test
+   script/myapp test t/foo.t
+
+Runs application tests from the C<t> directory.
+
+=item C<version>
+
+    mojo version
+
+List version information for installed core and optional modules, very useful
+for debugging.
+
 =back
 
 =head1 ATTRIBUTES
 
-L<Mojolicious::Commands> inherits all attributes from L<Mojo::Commands> and
+L<Mojolicious::Commands> inherits all attributes from L<Mojo::Command> and
 implements the following new ones.
 
 =head2 C<hint>
@@ -124,7 +177,7 @@ L<Mojolicious::Command>.
 
 =head1 METHODS
 
-L<Mojolicious::Commands> inherits all methods from L<Mojo::Commands>.
+L<Mojolicious::Commands> inherits all methods from L<Mojo::Command>.
 
 =head1 SEE ALSO
 
@@ -3,19 +3,69 @@ package Mojolicious::Controller;
 use strict;
 use warnings;
 
-use base 'MojoX::Dispatcher::Routes::Controller';
+use base 'Mojo::Base';
 
 use Mojo::ByteStream;
+use Mojo::Cookie::Response;
 use Mojo::Exception;
+use Mojo::Transaction::HTTP;
 use Mojo::URL;
+use Mojo::Util;
 
 require Carp;
 
+# Scalpel... blood bucket... priest.
+__PACKAGE__->attr([qw/app match/]);
+__PACKAGE__->attr(tx => sub { Mojo::Transaction::HTTP->new });
+
 # DEPRECATED in Comet!
 *finished        = \&on_finish;
 *receive_message = \&on_message;
 
-our $AUTOLOAD;
+# Reserved stash values
+my $STASH_RE = qr/
+    ^
+    (?:
+    action
+    |
+    app
+    |
+    cb
+    |
+    class
+    |
+    controller
+    |
+    data
+    |
+    exception
+    |
+    extends
+    |
+    format
+    |
+    handler
+    |
+    json
+    |
+    layout
+    |
+    method
+    |
+    namespace
+    |
+    partial
+    |
+    path
+    |
+    status
+    |
+    template
+    |
+    text
+    )
+    $
+    /x;
 
 # Is all the work done by the children?
 # No, not the whipping.
@@ -23,7 +73,7 @@ sub AUTOLOAD {
     my $self = shift;
 
     # Method
-    my ($package, $method) = $AUTOLOAD =~ /^([\w\:]+)\:\:(\w+)$/;
+    my ($package, $method) = our $AUTOLOAD =~ /^([\w\:]+)\:\:(\w+)$/;
 
     # Helper
     Carp::croak(qq/Can't locate object method "$method" via "$package"/)
@@ -37,6 +87,44 @@ sub DESTROY { }
 
 sub client { shift->app->client }
 
+# For the last time, I don't like lilacs!
+# Your first wife was the one who liked lilacs!
+# She also liked to shut up!
+sub cookie {
+    my ($self, $name, $value, $options) = @_;
+
+    # Shortcut
+    return unless $name;
+
+    # Response cookie
+    if (defined $value) {
+
+        # Cookie too big
+        $self->app->log->error(qq/Cookie "$name" is bigger than 4096 bytes./)
+          if length $value > 4096;
+
+        # Create new cookie
+        $options ||= {};
+        my $cookie = Mojo::Cookie::Response->new(
+            name  => $name,
+            value => $value,
+            %$options
+        );
+        $self->res->cookies($cookie);
+        return $self;
+    }
+
+    # Request cookie
+    unless (wantarray) {
+        return unless my $cookie = $self->req->cookie($name);
+        return $cookie->value;
+    }
+
+    # Request cookies
+    my @cookies = $self->req->cookie($name);
+    return map { $_->value } @cookies;
+}
+
 # Something's wrong, she's not responding to my poking stick.
 sub finish {
     my $self = shift;
@@ -51,6 +139,35 @@ sub finish {
     $tx->finish;
 }
 
+# You two make me ashamed to call myself an idiot.
+sub flash {
+    my $self = shift;
+
+    # Get
+    my $session = $self->stash->{'mojo.session'};
+    if ($_[0] && !defined $_[1] && !ref $_[0]) {
+        return unless $session && ref $session eq 'HASH';
+        return unless my $flash = $session->{old_flash};
+        return unless ref $flash eq 'HASH';
+        return $flash->{$_[0]};
+    }
+
+    # Initialize
+    $session = $self->session;
+    my $flash = $session->{flash};
+    $flash = {} unless $flash && ref $flash eq 'HASH';
+    $session->{flash} = $flash;
+
+    # Hash
+    return $flash unless @_;
+
+    # Set
+    my $values = exists $_[1] ? {@_} : $_[0];
+    $session->{flash} = {%$flash, %$values};
+
+    return $self;
+}
+
 # DEPRECATED in Comet!
 sub helper {
     my $self = shift;
@@ -95,6 +212,34 @@ sub on_message {
     return $self;
 }
 
+# Just make a simple cake. And this time, if someone's going to jump out of
+# it make sure to put them in *after* you cook it.
+sub param {
+    my $self = shift;
+    my $name = shift;
+
+    # Captures
+    my $p = $self->stash->{'mojo.captures'} || {};
+
+    # List
+    unless (defined $name) {
+        my %seen;
+        return sort grep { !$seen{$_}++ } keys %$p, $self->req->param;
+    }
+
+    # Override value
+    if (@_) {
+        $p->{$name} = $_[0];
+        return $self;
+    }
+
+    # Captured value
+    return $p->{$name} if exists $p->{$name};
+
+    # Param value
+    return $self->req->param($name);
+}
+
 # Is there an app for kissing my shiny metal ass?
 # Several!
 # Oooh!
@@ -358,6 +503,9 @@ sub rendered {
     return $self;
 }
 
+sub req { shift->tx->req }
+sub res { shift->tx->res }
+
 sub send_message {
     my $self = shift;
 
@@ -377,6 +525,104 @@ sub send_message {
     return $self;
 }
 
+# Why am I sticky and naked? Did I miss something fun?
+sub session {
+    my $self = shift;
+
+    # Get
+    my $stash   = $self->stash;
+    my $session = $stash->{'mojo.session'};
+    if ($_[0] && !defined $_[1] && !ref $_[0]) {
+        return unless $session && ref $session eq 'HASH';
+        return $session->{$_[0]};
+    }
+
+    # Initialize
+    $session = {} unless $session && ref $session eq 'HASH';
+    $stash->{'mojo.session'} = $session;
+
+    # Hash
+    return $session unless @_;
+
+    # Set
+    my $values = exists $_[1] ? {@_} : $_[0];
+    $stash->{'mojo.session'} = {%$session, %$values};
+
+    return $self;
+}
+
+sub signed_cookie {
+    my ($self, $name, $value, $options) = @_;
+
+    # Shortcut
+    return unless $name;
+
+    # Secret
+    my $secret = $self->app->secret;
+
+    # Response cookie
+    if (defined $value) {
+
+        # Sign value
+        my $signature = Mojo::Util::hmac_md5_sum $value, $secret;
+        $value = $value .= "--$signature";
+
+        # Create cookie
+        my $cookie = $self->cookie($name, $value, $options);
+        return $cookie;
+    }
+
+    # Request cookies
+    my @values = $self->cookie($name);
+    my @results;
+    for my $value (@values) {
+
+        # Check signature
+        if ($value =~ s/\-\-([^\-]+)$//) {
+            my $signature = $1;
+            my $check = Mojo::Util::hmac_md5_sum $value, $secret;
+
+            # Verified
+            if ($signature eq $check) { push @results, $value }
+
+            # Bad cookie
+            else {
+                $self->app->log->debug(
+                    qq/Bad signed cookie "$name", possible hacking attempt./);
+            }
+        }
+
+        # Not signed
+        else { $self->app->log->debug(qq/Cookie "$name" not signed./) }
+    }
+
+    return wantarray ? @results : $results[0];
+}
+
+# All this knowledge is giving me a raging brainer.
+sub stash {
+    my $self = shift;
+
+    # Initialize
+    $self->{stash} ||= {};
+
+    # Hash
+    return $self->{stash} unless @_;
+
+    # Get
+    return $self->{stash}->{$_[0]} unless @_ > 1 || ref $_[0];
+
+    # Set
+    my $values = ref $_[0] ? $_[0] : {@_};
+    for my $key (keys %$values) {
+        $self->app->log->debug(qq/Careful, "$key" is a reserved stash value./)
+          if $key =~ $STASH_RE;
+        $self->{stash}->{$key} = $values->{$key};
+    }
+
+    return $self;
+}
+
 # Behold, a time traveling machine.
 # Time? I can't go back there!
 # Ah, but this machine only goes forward in time.
@@ -388,7 +634,8 @@ sub url_for {
     my $target = shift || '';
 
     # Make sure we have a match for named routes
-    $self->match(MojoX::Routes::Match->new($self)->root($self->app->routes))
+    $self->match(
+        Mojolicious::Routes::Match->new($self)->root($self->app->routes))
       unless $self->match;
 
     # Path
@@ -431,9 +678,6 @@ sub write {
     $self->rendered;
 }
 
-# This calls for a party, baby.
-# I'm ordering 100 kegs, 100 hookers and 100 Elvis impersonators that aren't
-# above a little hooking should the occasion arise.
 sub write_chunk {
     my ($self, $chunk, $cb) = @_;
 
@@ -480,14 +724,34 @@ C<controller_class> in your application.
 
 =head1 ATTRIBUTES
 
-L<Mojolicious::Controller> inherits all attributes from
-L<MojoX::Dispatcher::Routes::Controller>.
+L<Mojolicious::Controller> inherits all attributes from L<Mojo::Base> and
+implements the following new ones.
+
+=head2 C<app>
+
+    my $app = $c->app;
+    $c      = $c->app(Mojolicious->new);
+
+A reference back to the application that dispatched to this controller.
+
+=head2 C<match>
+
+    my $m = $c->match;
+
+A L<Mojolicious::Routes::Match> object containing the routes results for the
+current request.
+
+=head2 C<tx>
+
+    my $tx = $c->tx;
+
+The transaction that is currently being processed, defaults to a
+L<Mojo::Transaction::HTTP> object.
 
 =head1 METHODS
 
-L<Mojolicious::Controller> inherits all methods from
-L<MojoX::Dispatcher::Routes::Controller> and implements the following new
-ones.
+L<Mojolicious::Controller> inherits all methods from L<Mojo::Base> and
+implements the following new ones.
 
 =head2 C<client>
 
@@ -504,20 +768,41 @@ A L<Mojo::Client> prepared for the current environment.
         $c->render_data($client->res->body);
     })->start;
 
-For async processing you can use C<finish>.
+Some environments such as L<Mojo::Server::Daemon> even allow async requests.
 
     $c->client->async->get('http://mojolicious.org' => sub {
         my $client = shift;
         $c->render_data($client->res->body);
-        $c->finish;
     })->start;
 
+=head2 C<cookie>
+
+    $c         = $c->cookie(foo => 'bar');
+    $c         = $c->cookie(foo => 'bar', {path => '/'});
+    my $value  = $c->cookie('foo');
+    my @values = $c->cookie('foo');
+
+Access request cookie values and create new response cookies.
+
 =head2 C<finish>
 
     $c->finish;
 
 Gracefully end WebSocket connection.
 
+=head2 C<flash>
+
+    my $flash = $c->flash;
+    my $foo   = $c->flash('foo');
+    $c        = $c->flash({foo => 'bar'});
+    $c        = $c->flash(foo => 'bar');
+
+Data storage persistent for the next request, stored in the session.
+
+    $c->flash->{foo} = 'bar';
+    my $foo = $c->flash->{foo};
+    delete $c->flash->{foo};
+
 =head2 C<on_finish>
 
     $c->on_finish(sub {...});
@@ -539,6 +824,15 @@ connection in progress.
         my ($self, $message) = @_;
     });
 
+=head2 C<param>
+
+    my @names = $c->param;
+    my $foo   = $c->param('foo');
+    my @foo   = $c->param('foo');
+    $c        = $c->param(foo => 'ba;r');
+
+Request parameters and routes captures.
+
 =head2 C<redirect_to>
 
     $c = $c->redirect_to('named');
@@ -561,7 +855,7 @@ Prepare a C<302> redirect response.
     $c->render('foo/bar');
     $c->render('foo/bar', format => 'html');
 
-This is a wrapper around L<MojoX::Renderer> exposing pretty much all
+This is a wrapper around L<Mojolicious::Renderer> exposing pretty much all
 functionality provided by it.
 It will set a default template to use based on the controller and action name
 or fall back to the route name.
@@ -583,7 +877,7 @@ Render binary data, similar to C<render_text> but data will not be encoded.
 Render the exception template C<exception.html.$handler>.
 Will set the status code to C<500> meaning C<Internal Server Error>.
 Takes a L<Mojo::Exception> object or error message and will fall back to
-rendering a static C<500> page using L<MojoX::Renderer::Static>.
+rendering a static C<500> page using L<Mojolicious::Static>.
 
 =head2 C<render_inner>
 
@@ -609,7 +903,7 @@ Render a data structure as JSON.
     
 Render the not found template C<not_found.html.$handler>.
 Also sets the response status code to C<404>, will fall back to rendering a
-static C<404> page using L<MojoX::Renderer::Static>.
+static C<404> page using L<Mojolicious::Static>.
 
 =head2 C<render_partial>
 
@@ -623,7 +917,7 @@ Same as C<render> but returns the rendered result.
     $c->render_static('images/logo.png');
     $c->render_static('../lib/MyApp.pm');
 
-Render a static file using L<MojoX::Dispatcher::Static> relative to the
+Render a static file using L<Mojolicious::Static> relative to the
 C<public> directory of your application.
 
 =head2 C<render_text>
@@ -641,6 +935,20 @@ See C<render_data> for an alternative without encoding.
 Finalize response and run C<after_dispatch> plugin hook.
 Note that this method is EXPERIMENTAL and might change without warning!
 
+=head2 C<req>
+
+    my $req = $c->req;
+
+Alias for C<$c->tx->req>.
+Usually refers to a L<Mojo::Message::Request> object.
+
+=head2 C<res>
+
+    my $res = $c->res;
+
+Alias for C<$c->tx->res>.
+Usually refers to a L<Mojo::Message::Response> object.
+
 =head2 C<send_message>
 
     $c = $c->send_message('Hi there!');
@@ -648,6 +956,43 @@ Note that this method is EXPERIMENTAL and might change without warning!
 Send a message via WebSocket, only works if there is currently a WebSocket
 connection in progress.
 
+=head2 C<session>
+
+    my $session = $c->session;
+    my $foo     = $c->session('foo');
+    $c          = $c->session({foo => 'bar'});
+    $c          = $c->session(foo => 'bar');
+
+Persistent data storage, by default stored in a signed cookie.
+Note that cookies are generally limited to 4096 bytes of data.
+
+    $c->session->{foo} = 'bar';
+    my $foo = $c->session->{foo};
+    delete $c->session->{foo};
+
+=head2 C<signed_cookie>
+
+    $c         = $c->signed_cookie(foo => 'bar');
+    $c         = $c->signed_cookie(foo => 'bar', {path => '/'});
+    my $value  = $c->signed_cookie('foo');
+    my @values = $c->signed_cookie('foo');
+
+Access signed request cookie values and create new signed response cookies.
+Cookies failing signature verification will be automatically discarded.
+
+=head2 C<stash>
+
+    my $stash = $c->stash;
+    my $foo   = $c->stash('foo');
+    $c        = $c->stash({foo => 'bar'});
+    $c        = $c->stash(foo => 'bar');
+
+Non persistent data storage and exchange.
+
+    $c->stash->{foo} = 'bar';
+    my $foo = $c->stash->{foo};
+    delete $c->stash->{foo};
+
 =head2 C<url_for>
 
     my $url = $c->url_for;
@@ -684,13 +1029,19 @@ Write dynamic content chunk wise with the C<chunked> C<Transfer-Encoding>
 which doesn't require a C<Content-Length> header, the optional drain callback
 will be invoked once all data has been written to the kernel send buffer or
 equivalent.
-An empty chunk marks the end of the stream.
+Note that this method is EXPERIMENTAL and might change without warning!
 
     $c->write_chunk('Hel');
     $c->write_chunk('lo!');
     $c->write_chunk('');
 
-Note that this method is EXPERIMENTAL and might change without warning!
+An empty chunk marks the end of the stream.
+
+    3
+    Hel
+    3
+    lo!
+    0
 
 =head1 SEE ALSO
 
@@ -167,13 +167,6 @@ C<DNS> server to use for non-blocking lookups.
 
     MOJO_DNS_SERVER=8.8.8.8
 
-=head2 C<MOJO_EPOLL>
-
-Force epoll mainloop for IO operations.
-Note that L<IO::Epoll> must be installed for epoll support.
-
-    MOJO_EPOLL=1
-
 =head2 C<MOJO_HOME>
 
 Home directory for the L<Mojolicious> application, should always contain a
@@ -181,13 +174,6 @@ path like C</home/sri/myapp>.
 
     MOJO_HOME=/home/sri/myapp
 
-=head2 C<MOJO_KQUEUE>
-
-Force kqueue mainloop for IO operations.
-Note that L<IO::KQueue> must be installed for kqueue support.
-
-    MOJO_KQUEUE=1
-
 =head2 C<MOJO_LOG_LEVEL>
 
 Log level for the L<Mojolicious> application, should contain a valid log
@@ -237,14 +223,6 @@ Note that L<Net::Rendezvous::Publish> must be installed for Bonjour support.
 
     MOJO_NO_BONJOUR=1
 
-=head2 C<MOJO_NO_IPV6>
-
-Disable IPv6 support, this might result in slightly better performance and
-less memory use.
-Note that L<IO::Socket::IP> must be installed for IPv6 support.
-
-    MOJO_NO_IPV6=1
-
 =head2 C<MOJO_NO_TLS>
 
 Disable TLS support, this might result in slightly better performance and
@@ -41,7 +41,7 @@ More capable editors can handle this accordingly and force the change.
 
 Quite possibly this oneliner.
 
-    sudo -s 'curl -L cpanmin.us | perl - "Mojolicious"'
+    curl -L cpanmin.us | perl - http://latest.mojolicio.us
 
 =head2 I think L<Mojolicious> is awesome, how can i support you guys?
 
@@ -49,9 +49,11 @@ Share your success story via blog or twitter, get more people hooked! :)
 
 =head2 I think i have found a bug, what should i do now?
 
-Prepare a test case demonstrating the bug, you are not expected to fix it
-yourself, but you'll have to make sure the developers can replicate your
-problem.
+First make sure you are using the latest version of L<Mojolicious>, it is
+quite likely that the bug has already been fixed.
+If that doesn't help prepare a test case demonstrating the bug, you are not
+expected to fix it yourself, but you'll have to make sure the developers can
+replicate your problem.
 Sending in your whole application generally does more harm than good, the
 C<t> directory of this distribution has many good examples for how to do it
 right.
@@ -448,6 +448,41 @@ executed more than once.
 
 Less commonly used and more powerful features.
 
+=head2 Chunked Transfer Encoding
+
+For very dynamic content you might not know the response C<Content-Length>
+in advance, thats where the C<chunked> C<Transfer-Encoding> comes in handy.
+A common use would be to send the C<head> section of an HTML document to the
+browser in advance and speed up preloading of referenced images and
+stylesheets.
+
+    $self->write_chunk('<html><head><title>Example</title>');
+    $self->write_chunk('<link href="example.css" rel="stylesheet"');
+    $self->write_chunk(' type="text/css" /></head>', sub {
+        my $self = shift;
+        $self->write_chunk('<body>Example</body></html>');
+        $self->write_chunk('');
+    });
+
+The optional drain callback ensures that all previous chunks have been
+written before processing continues.
+An empty chunk marks the end of the stream.
+
+    22
+    <html><head><title>Example</title>
+    29
+    <link href="example.css" rel="stylesheet"
+    19
+     type="text/css" /></head>
+    1C
+    <body>Example</body></html>
+    0
+
+Especially in combination with long connection timeouts this can be very
+useful for Comet (C<long polling>).
+Due to limitations in some web servers this might not work perfectly in all
+deployment environments.
+
 =head2 Encoding
 
 Templates stored in files are expected to be C<UTF-8> by default, but that
@@ -408,6 +408,37 @@ ones.
     $foo->route('/baz')->to(action => 'baz');
     $foo->route('/cde');
 
+=head2 Mojolicious::Lite routes
+
+L<Mojolicious::Lite> routes are in fact just a small convenience layer around
+everything described above and also part of the normal router.
+
+    # GET /foo -> {controller => 'foo', action => 'abc'}
+    $r->get('/foo')->to(controller => 'foo', action => 'abc');
+
+This makes the process of growing your L<Mojolicious::Lite> prototypes into
+full L<Mojolicious> applications very straightforward.
+
+    # POST /bar
+    $r->post('/bar' => sub {
+        my $self = shift;
+        $self->render(text => 'Just like a Mojolicious::Lite action!');
+    });
+
+Even the more abstract concepts are available.
+
+    # GET  /yada
+    # POST /yada
+    my $yada = $r->under('/yada');
+    $yada->get(sub {
+        my $self = shift;
+        $self->render(text => 'Hello!');
+    });
+    $yada->post(sub {
+        my $self = shift;
+        $self->render(text => 'Go away!');
+    });
+
 =head1 ADVANCED
 
 Less commonly used and more powerful features.
@@ -646,7 +677,7 @@ Now just load the plugin and you're done.
 You can restrict access to WebSocket handshakes using the C<websocket> method.
 
     # /ws (WebSocket handshake)
-    $r->route('/echo')->websocket->to(controller => 'foo', action => 'echo');
+    $r->websocket('/echo')->to(controller => 'foo', action => 'echo');
 
     # Controller
     package MyApp::Foo;
@@ -32,68 +32,6 @@ sub import {
     my $routes = $app->routes;
     $routes->namespace('');
 
-    # Route generator
-    my $route = sub {
-        my ($methods, @args) = @_;
-
-        my ($cb, $constraints, $defaults, $name, $pattern);
-        my $conditions = [];
-
-        # Route information
-        while (defined(my $arg = shift @args)) {
-
-            # First scalar is the pattern
-            if (!ref $arg && !$pattern) { $pattern = $arg }
-
-            # Scalar
-            elsif (!ref $arg && @args) {
-                push @$conditions, $arg, shift @args;
-            }
-
-            # Last scalar is the route name
-            elsif (!ref $arg) { $name = $arg }
-
-            # Callback
-            elsif (ref $arg eq 'CODE') { $cb = $arg }
-
-            # Constraints
-            elsif (ref $arg eq 'ARRAY') { $constraints = $arg }
-
-            # Defaults
-            elsif (ref $arg eq 'HASH') { $defaults = $arg }
-        }
-
-        # Defaults
-        $constraints ||= [];
-
-        # Defaults
-        $defaults ||= {};
-        $defaults->{cb} = $cb if $cb;
-
-        # Name
-        $name ||= '';
-
-        # Create bridge
-        return $routes =
-          $app->routes->bridge($pattern, {@$constraints})->over($conditions)
-          ->to($defaults)->name($name)
-          if !ref $methods && $methods eq 'under';
-
-        # WebSocket
-        my $websocket = 1 if !ref $methods && $methods eq 'websocket';
-        $methods = [] if $websocket;
-
-        # Create route
-        my $route =
-          $routes->route($pattern, {@$constraints})->over($conditions)
-          ->via($methods)->to($defaults)->name($name);
-
-        # WebSocket
-        $route->websocket if $websocket;
-
-        return $route;
-    };
-
     # Prepare exports
     my $caller = caller;
     no strict 'refs';
@@ -103,15 +41,18 @@ sub import {
     $app->static->default_static_class($caller);
     $app->renderer->default_template_class($caller);
 
+    # Root
+    my $root = $routes;
+
     # Export
     *{"${caller}::new"} = *{"${caller}::app"} = sub {$app};
-    *{"${caller}::any"} = sub { $route->(ref $_[0] ? shift : [], @_) };
-    *{"${caller}::get"} = sub { $route->('get', @_) };
+    *{"${caller}::any"} = sub { $routes->any(@_) };
+    *{"${caller}::get"} = sub { $routes->get(@_) };
     *{"${caller}::under"} = *{"${caller}::ladder"} =
-      sub { $route->('under', @_) };
+      sub { $routes = $root->under(@_) };
     *{"${caller}::plugin"}    = sub { $app->plugin(@_) };
-    *{"${caller}::post"}      = sub { $route->('post', @_) };
-    *{"${caller}::websocket"} = sub { $route->('websocket', @_) };
+    *{"${caller}::post"}      = sub { $routes->post(@_) };
+    *{"${caller}::websocket"} = sub { $routes->websocket(@_) };
 
     # We are most likely the app in a lite environment
     $ENV{MOJO_APP} = $app;
@@ -379,8 +320,15 @@ Routes can be restricted to specific request methods.
 All placeholders get compiled to a regex internally, with regex constraints
 this process can be easily customized.
 
-    # /*
-    any '/:bar' => [bar => qr/\d+/] => sub {
+    # /* (digits)
+    any '/:foo' => [foo => qr/\d+/] => sub {
+        my $self = shift;
+        my $foo  = $self->param('foo');
+        $self->render(text => "Our :foo placeholder matched $foo");
+    };
+
+    # /* (everything else)
+    any '/:bar' => [bar => qr/.*/] => sub {
         my $self = shift;
         my $bar  = $self->param('bar');
         $self->render(text => "Our :bar placeholder matched $bar");
@@ -49,6 +49,8 @@ Mojolicious::Plugin::AgentCondition - Agent Condition Plugin
 
 L<Mojolicous::Plugin::AgentCondition> is a routes condition for user agent
 based routes.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head1 METHODS
 
@@ -127,6 +127,8 @@ Mojolicious::Plugin::DefaultHelpers - Default Helpers Plugin
 
 L<Mojolicous::Plugin::DefaultHelpers> is a collection of renderer helpers for
 L<Mojolicious>.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head2 Helpers
 
@@ -118,6 +118,8 @@ It is based on L<Mojo::Template>, but extends it with some convenient syntax
 sugar designed specifically for L<Mojolicious>.
 It supports L<Mojolicious> template helpers and exposes the stash directly as
 perl variables.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head2 Options
 
@@ -110,6 +110,8 @@ Mojolicious::Plugin::EplRenderer - EPL Renderer Plugin
 
 L<Mojolicous::Plugin::EplRenderer> is a renderer for C<epl> templates.
 C<epl> templates are pretty much just raw L<Mojo::Template>.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head1 METHODS
 
@@ -46,6 +46,8 @@ Mojolicious::Plugin::PoweredBy - Powered By Plugin
 
 L<Mojolicous::Plugin::PoweredBy> is a plugin that adds an C<X-Powered-By>
 header which defaults to C<Mojolicious (Perl)>.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head2 Options
 
@@ -55,6 +55,8 @@ Mojolicious::Plugin::RequestTimer - Request Timer Plugin
 
 L<Mojolicous::Plugin::RequestTimer> is a plugin to gather and log request
 timing information.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 
 =head1 METHODS
 
@@ -13,6 +13,10 @@ use Mojo::Util 'xml_escape';
 sub register {
     my ($self, $app) = @_;
 
+    # Add "base_tag" helper
+    $app->helper(base_tag =>
+          sub { $self->_tag('base', href => shift->url_for->base, @_) });
+
     # Add "checkbox" helper
     $app->helper(
         check_box => sub {
@@ -69,23 +73,21 @@ sub register {
             my $c = shift;
 
             # CDATA
-            my $cb;
-            my $old = $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
-            $cb = sub { '<![CDATA[' . $old->() . ']]>' }
-              if $cb;
+            my $cb = sub {''};
+            if (ref $_[-1] && ref $_[-1] eq 'CODE') {
+                my $old = pop;
+                $cb = sub { "//<![CDATA[\n" . $old->() . "\n//]]>" }
+            }
 
             # Path
-            if (@_ % 2 ? ref $_[-1] ne 'CODE' : ref $_[-1] eq 'CODE') {
-                return $self->_tag(
-                    'script',
-                    src  => shift,
-                    type => 'text/javascript',
-                    @_
-                );
-            }
+            my $src;
+            $src = shift if @_ % 2;
 
-            # Block
-            $self->_tag('script', type => 'text/javascript', @_, $cb);
+            # Attributes
+            my %attrs = @_;
+            $attrs{src} = $src if $src;
+
+            $self->_tag('script', type => 'text/javascript', %attrs, $cb);
         }
     );
 
@@ -189,24 +191,30 @@ sub register {
 
             # CDATA
             my $cb;
-            my $old = $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
-            $cb = sub { '<![CDATA[' . $old->() . ']]>' }
-              if $cb;
+            if (ref $_[-1] && ref $_[-1] eq 'CODE') {
+                my $old = pop;
+                $cb = sub { "/*<![CDATA[*/\n" . $old->() . "\n/*]]>*/" }
+            }
 
             # Path
-            if (@_ % 2 ? ref $_[-1] ne 'CODE' : ref $_[-1] eq 'CODE') {
-                return $self->_tag(
-                    'link',
-                    href  => shift,
-                    media => 'screen',
-                    rel   => 'stylesheet',
-                    type  => 'text/css',
-                    @_
-                );
-            }
+            my $href;
+            $href = shift if @_ % 2;
 
-            # Block
-            $self->_tag('style', type => 'text/css', @_, $cb);
+            # Attributes
+            my %attrs = @_;
+
+            # Link
+            return $self->_tag(
+                'link',
+                href  => $href,
+                media => 'screen',
+                rel   => 'stylesheet',
+                type  => 'text/css',
+                %attrs
+            ) if $href;
+
+            # Style
+            $self->_tag('style', type => 'text/css', %attrs, $cb);
         }
     );
 
@@ -330,12 +338,22 @@ Mojolicious::Plugin::TagHelpers - Tag Helpers Plugin
 
 L<Mojolicous::Plugin::TagHelpers> is a collection of HTML5 tag helpers for
 L<Mojolicious>.
+This is a core plugin, that means it is always enabled and its code a good
+example for learning to build new plugins.
 Note that this module is EXPERIMENTAL and might change without warning!
 
 =head2 Helpers
 
 =over 4
 
+=item base_tag
+
+    <%= base_tag %>
+
+Generate C<base> tag refering to the current base URL.
+
+    <base href="http://localhost/cgi-bin/myapp.pl" />
+
 =item check_box
 
     <%= check_box employed => 1 %>
@@ -0,0 +1,506 @@
+package Mojolicious::Renderer;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use File::Spec;
+use Mojo::ByteStream 'b';
+use Mojo::Command;
+use Mojo::Home;
+use Mojo::JSON;
+use Mojo::Util 'encode';
+
+__PACKAGE__->attr(default_format => 'html');
+__PACKAGE__->attr([qw/default_handler default_template_class/]);
+__PACKAGE__->attr(detect_templates => 1);
+__PACKAGE__->attr(encoding         => 'UTF-8');
+__PACKAGE__->attr(handler          => sub { {} });
+__PACKAGE__->attr(helper           => sub { {} });
+__PACKAGE__->attr(layout_prefix    => 'layouts');
+__PACKAGE__->attr(root             => '/');
+
+# This is not how Xmas is supposed to be.
+# In my day Xmas was about bringing people together, not blowing them apart.
+sub new {
+    my $self = shift->SUPER::new(@_);
+
+    # Data
+    $self->add_handler(
+        data => sub {
+            my ($r, $c, $output, $options) = @_;
+            $$output = $options->{data};
+        }
+    );
+
+    # JSON
+    $self->add_handler(
+        json => sub {
+            my ($r, $c, $output, $options) = @_;
+            $$output = Mojo::JSON->new->encode($options->{json});
+        }
+    );
+
+    # Text
+    $self->add_handler(
+        text => sub {
+            my ($r, $c, $output, $options) = @_;
+            $$output = $options->{text};
+        }
+    );
+
+    return $self;
+}
+
+sub add_handler {
+    my ($self, $name, $cb) = @_;
+    $self->handler->{$name} = $cb;
+    return $self;
+}
+
+sub add_helper {
+    my ($self, $name, $cb) = @_;
+    $self->helper->{$name} = $cb;
+    return $self;
+}
+
+sub get_inline_template {
+    my ($self, $options, $template) = @_;
+    return Mojo::Command->new->get_data($template,
+        $self->_detect_template_class($options));
+}
+
+# Bodies are for hookers and fat people.
+sub render {
+    my ($self, $c, $args) = @_;
+
+    # Stash
+    my $stash = $c->stash;
+
+    # Arguments
+    $args ||= {};
+
+    # Content
+    my $content = $stash->{'mojo.content'} ||= {};
+
+    # Partial
+    my $partial = $args->{partial};
+
+    # Localize extends and layout
+    local $stash->{layout}  = $partial ? undef : $stash->{layout};
+    local $stash->{extends} = $partial ? undef : $stash->{extends};
+
+    # Merge stash and arguments
+    while (my ($key, $value) = each %$args) {
+        $stash->{$key} = $value;
+    }
+
+    # Template
+    my $template = delete $stash->{template};
+
+    # Template class
+    my $class = $stash->{template_class};
+
+    # Format
+    my $format = $stash->{format} || $self->default_format;
+
+    # Handler
+    my $handler = $stash->{handler};
+
+    # Data
+    my $data = delete $stash->{data};
+
+    # JSON
+    my $json = delete $stash->{json};
+
+    # Text
+    my $text = delete $stash->{text};
+
+    # Inline
+    my $inline = delete $stash->{inline};
+    $handler = $self->default_handler if defined $inline && !defined $handler;
+
+    my $options = {
+        template       => $template,
+        format         => $format,
+        handler        => $handler,
+        encoding       => $self->encoding,
+        inline         => $inline,
+        template_class => $class
+    };
+    my $output;
+
+    # Text
+    if (defined $text) {
+
+        # Render
+        $self->handler->{text}->($self, $c, \$output, {text => $text});
+
+        # Extends
+        $content->{content} = b("$output")
+          if ($c->stash->{extends} || $c->stash->{layout});
+    }
+
+    # Data
+    elsif (defined $data) {
+
+        # Render
+        $self->handler->{data}->($self, $c, \$output, {data => $data});
+
+        # Extends
+        $content->{content} = b("$output")
+          if ($c->stash->{extends} || $c->stash->{layout});
+    }
+
+    # JSON
+    elsif (defined $json) {
+
+        # Render
+        $self->handler->{json}->($self, $c, \$output, {json => $json});
+        $format = 'json';
+
+        # Extends
+        $content->{content} = b("$output")
+          if ($c->stash->{extends} || $c->stash->{layout});
+    }
+
+    # Template or templateless handler
+    else {
+
+        # Render
+        return unless $self->_render_template($c, \$output, $options);
+
+        # Extends
+        $content->{content} = b("$output")
+          if ($c->stash->{extends} || $c->stash->{layout});
+    }
+
+    # Extends
+    while ((my $extends = $self->_extends($c)) && !$json && !$data) {
+
+        # Stash
+        my $stash = $c->stash;
+
+        # Template class
+        $class = $stash->{template_class};
+        $options->{template_class} = $class;
+
+        # Handler
+        $handler = $stash->{handler};
+        $options->{handler} = $handler;
+
+        # Format
+        $format = $stash->{format} || $self->default_format;
+        $options->{format} = $format;
+
+        # Template
+        $options->{template} = $extends;
+
+        # Render
+        $self->_render_template($c, \$output, $options);
+    }
+
+    # Encoding (JSON is already encoded)
+    unless ($partial) {
+        my $encoding = $options->{encoding};
+        encode $encoding, $output if $encoding && $output && !$json && !$data;
+    }
+
+    # Type
+    my $type = $c->app->types->type($format) || 'text/plain';
+
+    return ($output, $type);
+}
+
+sub template_name {
+    my ($self, $options) = @_;
+
+    # Template
+    return unless my $template = $options->{template} || '';
+
+    # Format
+    return unless my $format = $options->{format};
+
+    # Handler
+    my $handler = $options->{handler};
+
+    # File
+    my $file = "$template.$format";
+    $file = "$file.$handler" if $handler;
+
+    return $file;
+}
+
+sub template_path {
+    my $self = shift;
+    return unless my $name = $self->template_name(shift);
+    return File::Spec->catfile($self->root, split '/', $name);
+}
+
+sub _detect_handler {
+    my ($self, $options) = @_;
+
+    # Disabled
+    return unless $self->detect_templates;
+
+    # Template class
+    my $class = $self->_detect_template_class($options);
+
+    # Templates
+    my $templates = $self->{_templates};
+    unless ($templates) {
+        $templates = $self->{_templates} =
+          Mojo::Home->new->parse($self->root)->list_files;
+    }
+
+    # Inline templates
+    my $inline = $self->{_inline_templates}->{$class}
+      ||= $self->_list_inline_templates($class);
+
+    # Detect
+    return unless my $file = $self->template_name($options);
+    $file = quotemeta $file;
+    for my $template (@$templates, @$inline) {
+        if ($template =~ /^$file\.(\w+)$/) { return $1 }
+    }
+
+    return;
+}
+
+# You are hereby conquered.
+# Please line up in order of how much beryllium it takes to kill you.
+sub _detect_template_class {
+    my ($self, $options) = @_;
+    return
+         $options->{template_class}
+      || $ENV{MOJO_TEMPLATE_CLASS}
+      || $self->default_template_class
+      || 'main';
+}
+
+sub _extends {
+    my ($self, $c) = @_;
+
+    # Layout
+    my $stash = $c->stash;
+    if (my $layout = delete $stash->{layout}) {
+        $stash->{extends} ||= $self->layout_prefix . '/' . $layout;
+    }
+
+    # Extends
+    return delete $stash->{extends};
+}
+
+sub _list_inline_templates {
+    my ($self, $class) = @_;
+
+    # Get all
+    my $all = Mojo::Command->new->get_all_data($class);
+
+    # List
+    return [keys %$all];
+}
+
+# Well, at least here you'll be treated with dignity.
+# Now strip naked and get on the probulator.
+sub _render_template {
+    my ($self, $c, $output, $options) = @_;
+
+    # Renderer
+    my $handler =
+         $options->{handler}
+      || $self->_detect_handler($options)
+      || $self->default_handler;
+    $options->{handler} = $handler;
+    my $renderer = $self->handler->{$handler};
+
+    # No handler
+    unless ($renderer) {
+        $c->app->log->error(qq/No handler for "$handler" available./);
+        return;
+    }
+
+    # Render
+    return unless $renderer->($self, $c, $output, $options);
+
+    # Success!
+    return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Renderer - MIME Type Based Renderer
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Renderer;
+
+    my $renderer = Mojolicious::Renderer->new;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Renderer> is the standard L<Mojolicious> renderer.
+It turns your stashed data structures into content.
+See L<Mojolicious::Guide::Rendering> for more.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Renderer> implements the following attributes.
+
+=head2 C<default_format>
+
+    my $default = $renderer->default_format;
+    $renderer   = $renderer->default_format('html');
+
+The default format to render if C<format> is not set in the stash.
+The renderer will use L<Mojolicious::Types> to look up the content MIME type.
+
+=head2 C<default_handler>
+
+    my $default = $renderer->default_handler;
+    $renderer   = $renderer->default_handler('epl');
+
+The default template handler to use for rendering in cases where auto
+detection doesn't work, like for C<inline> templates.
+
+=over 4
+
+=item epl
+
+C<Embedded Perl Lite> handled by L<Mojolicious::Plugin::EplRenderer>.
+
+=item ep
+
+C<Embedded Perl> handled by L<Mojolicious::Plugin::EpRenderer>.
+
+=back
+
+=head2 C<default_template_class>
+
+    my $default = $renderer->default_template_class;
+    $renderer   = $renderer->default_template_class('main');
+
+The renderer will use this class to look for templates in the C<DATA>
+section.
+
+=head2 C<detect_templates>
+
+    my $detect = $renderer->detect_templates;
+    $renderer  = $renderer->detect_templates(1);
+
+Template auto detection, the renderer will try to select the right template
+and renderer automatically.
+
+=head2 C<encoding>
+
+    my $encoding = $renderer->encoding;
+    $renderer    = $renderer->encoding('koi8-r');
+
+Will encode the content if set, defaults to C<UTF-8>.
+
+=head2 C<handler>
+
+    my $handler = $renderer->handler;
+    $renderer   = $renderer->handler({epl => sub { ... }});
+
+Registered handlers.
+
+=head2 C<helper>
+
+    my $helper = $renderer->helper;
+    $renderer  = $renderer->helper({url_for => sub { ... }});
+
+Registered helpers.
+
+=head2 C<layout_prefix>
+
+    my $prefix = $renderer->layout_prefix;
+    $renderer  = $renderer->layout_prefix('layouts');
+
+Directory to look for layouts in, defaults to C<layouts>.
+
+=head2 C<root>
+
+   my $root  = $renderer->root;
+   $renderer = $renderer->root('/foo/bar/templates');
+   
+Directory to look for templates in.
+
+=head1 METHODS
+
+L<Mojolicious::Renderer> inherits all methods from L<Mojo::Base> and implements the
+following ones.
+
+=head2 C<new>
+
+    my $renderer = Mojolicious::Renderer->new;
+
+Construct a new renderer.
+
+=head2 C<add_handler>
+
+    $renderer = $renderer->add_handler(epl => sub { ... });
+    
+Add a new handler to the renderer.
+See L<Mojolicious::Plugin::EpRenderer> for a sample renderer.
+
+=head2 C<add_helper>
+
+    $renderer = $renderer->add_helper(url_for => sub { ... });
+
+Add a new helper to the renderer.
+See L<Mojolicious::Plugin::EpRenderer> for sample helpers.
+
+=head2 C<get_inline_template>
+
+    my $template = $renderer->get_inline_template({
+        template       => 'foo/bar',
+        format         => 'html',
+        handler        => 'epl'
+        template_class => 'main'
+    }, 'foo.html.ep');
+
+Get an inline template by name, usually used by handlers.
+
+=head2 C<render>
+
+    my ($output, $type) = $renderer->render($c);
+    my ($output, $type) = $renderer->render($c, $args);
+
+Render output through one of the Mojo renderers.
+This renderer requires some configuration, at the very least you will need to
+have a default C<format> and a default C<handler> as well as a C<template> or
+C<text>/C<json>.
+See L<Mojolicious::Controller> for a more user friendly interface.
+
+=head2 C<template_name>
+
+    my $template = $renderer->template_name({
+        template => 'foo/bar',
+        format   => 'html',
+        handler  => 'epl'
+    });
+    
+Builds a template name based on an options hash with C<template>, C<format>
+and C<handler>.
+
+=head2 C<template_path>
+
+    my $path = $renderer->template_path({
+        template => 'foo/bar',
+        format   => 'html',
+        handler  => 'epl'
+    });
+
+Builds a full template path based on an options hash with C<template>,
+C<format> and C<handler>.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,322 @@
+package Mojolicious::Routes::Match;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use Carp 'croak';
+use Mojo::Util qw/decode url_unescape/;
+use Mojo::URL;
+use Scalar::Util 'weaken';
+
+__PACKAGE__->attr(captures => sub { {} });
+__PACKAGE__->attr([qw/endpoint root/]);
+__PACKAGE__->attr(stack => sub { [] });
+
+# I'm Bender, baby, please insert liquor!
+sub new {
+    my $self = shift->SUPER::new();
+    my $c    = shift;
+
+    # Controller
+    $self->{_controller} = $c;
+    weaken $self->{_controller};
+
+    # Path
+    unless ($self->{_path} = shift) {
+        $self->{_path} = $c->req->url->path->to_string;
+        url_unescape $self->{_path};
+        decode 'UTF8', $self->{_path};
+    }
+
+    return $self;
+}
+
+# Life can be hilariously cruel.
+sub match {
+    my ($self, $r) = @_;
+
+    # Shortcut
+    return unless $r;
+
+    # Dictionary
+    my $dictionary = $self->{_dictionary} ||= $r->dictionary;
+
+    # Root
+    $self->root($r) unless $self->root;
+
+    # Path
+    my $path = $self->{_path};
+
+    # Match
+    my $captures = $r->pattern->shape_match(\$path);
+
+    # No match
+    return unless $captures;
+
+    # Conditions
+    for (my $i = 0; $i < @{$r->conditions}; $i += 2) {
+        my $name      = $r->conditions->[$i];
+        my $value     = $r->conditions->[$i + 1];
+        my $condition = $dictionary->{$name};
+
+        # No condition
+        return unless $condition;
+
+        # Match
+        return
+          if !$condition->($r, $self->{_controller}, $self->captures, $value);
+    }
+
+    # Partial
+    if (my $partial = $r->partial) {
+        $captures->{$partial} = $path;
+        $path = '';
+    }
+    $self->{_path} = $path;
+
+    # Merge captures
+    $captures = {%{$self->captures}, %$captures};
+    $self->captures($captures);
+
+    # Format
+    if ($r->is_endpoint && !$r->pattern->format) {
+        if ($path =~ /^\.([^\/]+)$/) {
+            $self->captures->{format} = $1;
+            $self->{_path} = '';
+        }
+    }
+    $self->captures->{format} ||= $r->pattern->format if $r->pattern->format;
+
+    # Update stack
+    if ($r->inline || ($r->is_endpoint && $self->_is_path_empty)) {
+        push @{$self->stack}, {%$captures};
+        delete $captures->{cb};
+        delete $captures->{app};
+    }
+
+    # Waypoint match
+    if ($r->block && $self->_is_path_empty) {
+        $self->endpoint($r);
+        return $self;
+    }
+
+    # Match children
+    my $snapshot = [@{$self->stack}];
+    for my $child (@{$r->children}) {
+
+        # Match
+        $self->match($child);
+
+        # Endpoint found
+        return $self if $self->endpoint;
+
+        # Reset path
+        $self->{_path} = $path;
+
+        # Reset stack
+        if ($r->parent) { $self->stack([@$snapshot]) }
+        else {
+            $self->captures({});
+            $self->stack([]);
+        }
+    }
+
+    $self->endpoint($r) if $r->is_endpoint && $self->_is_path_empty;
+
+    return $self;
+}
+
+sub url_for {
+    my $self     = shift;
+    my $endpoint = $self->endpoint;
+    my $values   = {};
+    my $name     = undef;
+
+    # Single argument
+    if (@_ == 1) {
+
+        # Hash
+        $values = shift if ref $_[0] eq 'HASH';
+
+        # Name
+        $name = $_[0] if $_[0];
+    }
+
+    # Multiple arguments
+    elsif (@_ > 1) {
+
+        # Odd
+        if (@_ % 2) {
+            $name   = shift;
+            $values = {@_};
+        }
+
+        # Even
+        else {
+
+            # Name and hashref
+            if (ref $_[1] eq 'HASH') {
+                $name   = shift;
+                $values = shift;
+            }
+
+            # Just values
+            else { $values = {@_} }
+
+        }
+    }
+
+    # Captures
+    my $captures = $self->captures;
+
+    # Named
+    if ($name) {
+
+        # Current route
+        if ($name eq 'current') { $name = undef }
+
+        # Find
+        else {
+            $captures = {};
+            croak qq/Route "$name" used in url_for does not exist/
+              unless $endpoint = $self->_find_route($name);
+        }
+    }
+
+    # Merge values
+    $values = {%$captures, format => undef, %$values};
+
+    # URL
+    my $url = Mojo::URL->new;
+
+    # No endpoint
+    return $url unless $endpoint;
+
+    # Base
+    $url->base($self->{_controller}->req->url->base->clone);
+    my $base = $url->base;
+    $url->base->userinfo(undef);
+
+    # Render
+    my $path = $endpoint->render($url->path->to_string, $values);
+    $url->path->parse($path);
+
+    # Fix scheme
+    if ($endpoint->is_websocket) {
+        $base->scheme(($base->scheme || '') eq 'https' ? 'wss' : 'ws');
+    }
+
+    # Fix paths
+    unshift @{$url->path->parts}, @{$base->path->parts};
+    $base->path->parts([]);
+
+    return $url;
+}
+
+sub _find_route {
+    my ($self, $name) = @_;
+
+    # Find endpoint
+    my @children = ($self->root);
+    while (my $child = shift @children) {
+
+        # Match
+        return $child if ($child->name || '') eq $name;
+
+        # Append
+        push @children, @{$child->children};
+    }
+
+    # Not found
+    return;
+}
+
+sub _is_path_empty {
+    my $self = shift;
+    return 1 if !length $self->{_path} || $self->{_path} eq '/';
+    return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Routes::Match - Routes Visitor
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Routes::Match;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes::Match> is a visitor for L<Mojolicious::Routes>
+structures.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes::Match> implements the following attributes.
+
+=head2 C<captures>
+
+    my $captures = $m->captures;
+    $m           = $m->captures({foo => 'bar'});
+
+Captured parameters.
+
+=head2 C<endpoint>
+
+    my $endpoint = $m->endpoint;
+    $m           = $m->endpoint(Mojolicious::Routes->new);
+
+The routes endpoint that actually matched.
+
+=head2 C<root>
+
+    my $root = $m->root;
+    $m       = $m->root($routes);
+
+The root of the routes tree.
+
+=head2 C<stack>
+
+    my $stack = $m->stack;
+    $m        = $m->stack([{foo => 'bar'}]);
+
+Captured parameters with nesting history.
+
+=head1 METHODS
+
+L<Mojolicious::Routes::Match> inherits all methods from L<Mojo::Base> and
+implements the following ones.
+
+=head2 C<new>
+
+    my $m = Mojolicious::Routes::Match->new(Mojolicious:Controller->new);
+
+Construct a new match object.
+
+=head2 C<match>
+
+    $m->match(Mojolicious::Routes->new);
+
+Match against a routes tree.
+
+=head2 C<url_for>
+
+    my $url = $m->url_for;
+    my $url = $m->url_for(foo => 'bar');
+    my $url = $m->url_for({foo => 'bar'});
+    my $url = $m->url_for('named');
+    my $url = $m->url_for('named', foo => 'bar');
+    my $url = $m->url_for('named', {foo => 'bar'});
+
+Render matching route with parameters into a L<Mojo::URL> object.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,453 @@
+package Mojolicious::Routes::Pattern;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use constant DEBUG => $ENV{MOJO_ROUTES_DEBUG} || 0;
+
+__PACKAGE__->attr(defaults => sub { {} });
+__PACKAGE__->attr([qw/format pattern regex/]);
+__PACKAGE__->attr(quote_end      => ')');
+__PACKAGE__->attr(quote_start    => '(');
+__PACKAGE__->attr(relaxed_start  => '.');
+__PACKAGE__->attr(reqs           => sub { {} });
+__PACKAGE__->attr(symbol_start   => ':');
+__PACKAGE__->attr(symbols        => sub { [] });
+__PACKAGE__->attr(tree           => sub { [] });
+__PACKAGE__->attr(wildcard_start => '*');
+
+# This is the worst kind of discrimination. The kind against me!
+sub new {
+    my $self = shift->SUPER::new();
+    $self->parse(@_);
+    return $self;
+}
+
+sub match {
+    my ($self, $path) = @_;
+
+    # Match
+    my $result = $self->shape_match(\$path);
+
+    # Endpoint
+    return $result if !$path || $path eq '/';
+
+    # Partial or no match
+    return;
+}
+
+sub parse {
+    my $self    = shift;
+    my $pattern = shift;
+
+    # Shortcut
+    return $self unless defined $pattern;
+
+    # Make sure pattern starts with a slash
+    $pattern = "/$pattern" unless $pattern =~ /^\//;
+
+    # Format
+    if ($pattern =~ /\.([^\/\)]+)$/) { $self->format($1) }
+
+    # Requirements
+    my $reqs = ref $_[0] eq 'HASH' ? $_[0] : {@_};
+    $self->reqs($reqs);
+
+    # Tokenize
+    $self->pattern($pattern);
+    $self->_tokenize;
+
+    return $self;
+}
+
+sub render {
+    my ($self, $values) = @_;
+
+    # Merge values with defaults
+    $values ||= {};
+    $values = {%{$self->defaults}, %$values};
+
+    my $string   = '';
+    my $optional = 1;
+    for my $token (reverse @{$self->tree}) {
+        my $op       = $token->[0];
+        my $rendered = '';
+
+        # Slash
+        if ($op eq 'slash') {
+            $rendered = '/' unless $optional;
+        }
+
+        # Text
+        elsif ($op eq 'text') {
+            $rendered = $token->[1];
+            $optional = 0;
+        }
+
+        # Relaxed, symbol or wildcard
+        elsif ($op eq 'relaxed' || $op eq 'symbol' || $op eq 'wildcard') {
+            my $name = $token->[1];
+            $rendered = $values->{$name};
+            $rendered = '' unless defined $rendered;
+
+            my $default = $self->defaults->{$name};
+            $default = '' unless defined $default;
+
+            $optional = 0 unless $default eq $rendered;
+            $rendered = '' if $optional && $default eq $rendered;
+        }
+
+        $string = "$rendered$string";
+    }
+
+    return $string || '/';
+}
+
+sub shape_match {
+    my ($self, $pathref) = @_;
+
+    # Debug
+    if (DEBUG) {
+        my $pattern = $self->pattern || '';
+        warn "    [$$pathref] -> [$pattern]\n";
+    }
+
+    # Compile on demand
+    $self->_compile unless $self->regex;
+
+    my $regex = $self->regex;
+
+    # Debug
+    warn "    $regex\n" if DEBUG;
+
+    # Match
+    if (my @captures = $$pathref =~ /$regex/) {
+
+        # Substitute
+        $$pathref =~ s/$regex//;
+
+        # Merge captures
+        my $result = {%{$self->defaults}};
+        for my $symbol (@{$self->symbols}) {
+
+            # No captures
+            last unless @captures;
+
+            # Merge
+            my $capture = shift @captures;
+            $result->{$symbol} = $capture if defined $capture;
+        }
+        return $result;
+    }
+
+    return;
+}
+
+sub _compile {
+    my $self = shift;
+
+    my $block    = '';
+    my $regex    = '';
+    my $optional = 1;
+    for my $token (reverse @{$self->tree}) {
+        my $op       = $token->[0];
+        my $compiled = '';
+
+        # Slash
+        if ($op eq 'slash') {
+
+            # Full block
+            $block = $optional ? "(?:/$block)?" : "/$block";
+
+            $regex = "$block$regex";
+            $block = '';
+
+            next;
+        }
+
+        # Text
+        elsif ($op eq 'text') {
+            $compiled = $token->[1];
+            $optional = 0;
+        }
+
+        # Symbol
+        elsif ($op eq 'relaxed' || $op eq 'symbol' || $op eq 'wildcard') {
+            my $name = $token->[1];
+
+            unshift @{$self->symbols}, $name;
+
+            # Relaxed
+            if ($op eq 'relaxed') { $compiled = '([^\/]+)' }
+
+            # Symbol
+            elsif ($op eq 'symbol') { $compiled = '([^\/\.]+)' }
+
+            # Wildcard
+            elsif ($op eq 'wildcard') { $compiled = '(.+)' }
+
+            my $req = $self->reqs->{$name};
+            $compiled = "($req)" if $req;
+
+            $optional = 0 unless exists $self->defaults->{$name};
+
+            $compiled .= '?' if $optional;
+        }
+
+        # Add to block
+        $block = "$compiled$block";
+    }
+
+    # Not rooted with a slash
+    $regex = "$block$regex" if $block;
+
+    $regex = qr/^$regex/;
+    $self->regex($regex);
+
+    return $self;
+}
+
+sub _tokenize {
+    my $self = shift;
+
+    my $pattern        = $self->pattern;
+    my $quote_end      = $self->quote_end;
+    my $quote_start    = $self->quote_start;
+    my $relaxed_start  = $self->relaxed_start;
+    my $symbol_start   = $self->symbol_start;
+    my $wildcard_start = $self->wildcard_start;
+
+    my $tree  = [];
+    my $state = 'text';
+
+    my $quoted = 0;
+    while (length(my $char = substr $pattern, 0, 1, '')) {
+
+        # Inside a symbol
+        my $symbol = 0;
+        $symbol = 1
+          if $state eq 'relaxed'
+              || $state eq 'symbol'
+              || $state eq 'wildcard';
+
+        # Quote start
+        if ($char eq $quote_start) {
+            $quoted = 1;
+            $state  = 'symbol';
+            push @$tree, ['symbol', ''];
+            next;
+        }
+
+        # Symbol start
+        if ($char eq $symbol_start) {
+            push @$tree, ['symbol', ''] if $state ne 'symbol';
+            $state = 'symbol';
+            next;
+        }
+
+        # Relaxed start
+        if ($quoted && $char eq $relaxed_start) {
+
+            # Upgrade relaxed to wildcard
+            if ($state eq 'symbol') {
+                $state = 'relaxed';
+                $tree->[-1]->[0] = 'relaxed';
+                next;
+            }
+
+        }
+
+        # Wildcard start
+        if ($quoted && $char eq $wildcard_start) {
+
+            # Upgrade relaxed to wildcard
+            if ($state eq 'symbol') {
+                $state = 'wildcard';
+                $tree->[-1]->[0] = 'wildcard';
+                next;
+            }
+
+        }
+
+        # Quote end
+        if ($char eq $quote_end) {
+            $quoted = 0;
+            $state  = 'text';
+            next;
+        }
+
+        # Slash
+        if ($char eq '/') {
+            push @$tree, ['slash'];
+            $state = 'text';
+            next;
+        }
+
+        # Relaxed, symbol or wildcard
+        elsif ($symbol && $char =~ /\w/) {
+            $tree->[-1]->[-1] .= $char;
+            next;
+        }
+
+        # Text
+        else {
+
+            $state = 'text';
+
+            # New text element
+            unless ($tree->[-1]->[0] eq 'text') {
+                push @$tree, ['text', $char];
+                next;
+            }
+
+            # More text
+            $tree->[-1]->[-1] .= $char;
+        }
+    }
+
+    $self->tree($tree);
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Routes::Pattern - Routes Pattern
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Routes::Pattern;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes::Pattern> is a container for routes pattern which are
+used to match paths against.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes::Pattern> implements the following attributes.
+
+=head2 C<defaults>
+
+    my $defaults = $pattern->defaults;
+    $pattern     = $pattern->defaults({foo => 'bar'});
+
+Default parameters.
+
+=head2 C<pattern>
+
+    my $pattern = $pattern->pattern;
+    $pattern    = $pattern->pattern('/(foo)/(bar)');
+
+Raw unparsed pattern.
+
+=head2 C<quote_end>
+
+    my $quote = $pattern->quote_end;
+    $pattern  = $pattern->quote_end(']');
+
+Character indicating the end of a quoted placeholder, defaults to C<)>.
+
+=head2 C<quote_start>
+
+    my $quote = $pattern->quote_start;
+    $pattern  = $pattern->quote_start('[');
+
+Character indicating the start of a quoted placeholder, defaults to C<(>.
+
+=head2 C<regex>
+
+    my $regex = $pattern->regex;
+    $pattern  = $pattern->regex(qr/\/foo/);
+
+Pattern in compiled regex form.
+
+=head2 C<relaxed_start>
+
+    my $relaxed = $pattern->relaxed_start;
+    $pattern    = $pattern->relaxed_start('*');
+
+Character indicating a relaxed placeholder, defaults to C<.>.
+
+=head2 C<reqs>
+
+    my $reqs = $pattern->reqs;
+    $pattern = $pattern->reqs({foo => qr/\w+/});
+
+Regex constraints.
+
+=head2 C<symbol_start>
+
+    my $symbol = $pattern->symbol_start;
+    $pattern   = $pattern->symbol_start(':');
+
+Character indicating a placeholder, defaults to C<:>.
+
+=head2 C<symbols>
+
+    my $symbols = $pattern->symbols;
+    $pattern    = $pattern->symbols(['foo', 'bar']);
+
+Placeholder names.
+
+=head2 C<tree>
+
+    my $tree = $pattern->tree;
+    $pattern = $pattern->tree([ ... ]);
+
+Pattern in parsed form.
+
+=head2 C<wildcard_start>
+
+    my $wildcard = $pattern->wildcard_start;
+    $pattern     = $pattern->wildcard_start('*');
+
+Character indicating the start of a wildcard placeholder, defaults to C<*>.
+
+=head1 METHODS
+
+L<Mojolicious::Routes::Pattern> inherits all methods from L<Mojo::Base> and
+implements the following ones.
+
+=head2 C<new>
+
+    my $pattern = Mojolicious::Routes::Pattern->new('/:controller/:action',
+        action => qr/\w+/
+    );
+
+Construct a new pattern object.
+
+=head2 C<match>
+
+    my $result = $pattern->match('/foo/bar');
+
+Match pattern against a path.
+
+=head2 C<parse>
+
+    $pattern = $pattern->parse('/:controller/:action', action => qr/\w+/);
+
+Parse a raw pattern.
+
+=head2 C<render>
+
+    my $path = $pattern->render({action => 'foo'});
+
+Render pattern into a path with parameters.
+
+=head2 C<shape_match>
+
+    my $result = $pattern->shape_match(\$path);
+
+Match pattern against a path and remove matching parts.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,986 @@
+package Mojolicious::Routes;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use Mojo::Exception;
+use Mojo::Loader;
+use Mojo::URL;
+use Mojo::Util 'camelize';
+use Mojolicious::Routes::Match;
+use Mojolicious::Routes::Pattern;
+use Scalar::Util 'weaken';
+
+__PACKAGE__->attr([qw/block inline parent partial/]);
+__PACKAGE__->attr([qw/children conditions/] => sub { [] });
+__PACKAGE__->attr(controller_base_class => 'Mojolicious::Controller');
+__PACKAGE__->attr(dictionary => sub { {} });
+__PACKAGE__->attr(hidden => sub { [qw/new app attr render req res stash tx/] }
+);
+__PACKAGE__->attr('namespace');
+__PACKAGE__->attr(pattern => sub { Mojolicious::Routes::Pattern->new });
+
+# Yet thanks to my trusty safety sphere,
+# I sublibed with only tribial brain dablage.
+sub new {
+    my $self = shift->SUPER::new();
+
+    # Parse
+    $self->parse(@_);
+
+    # Method condition
+    $self->add_condition(
+        method => sub {
+            my ($r, $c, $captures, $methods) = @_;
+
+            # Methods
+            return unless $methods && ref $methods eq 'ARRAY';
+
+            # Match
+            my $m = lc $c->req->method;
+            $m = 'get' if $m eq 'head';
+            for my $method (@$methods) {
+                return 1 if $method eq $m;
+            }
+
+            # Nothing
+            return;
+        }
+    );
+
+    # WebSocket condition
+    $self->add_condition(
+        websocket => sub {
+            my ($r, $c, $captures) = @_;
+
+            # WebSocket
+            return 1 if $c->tx->is_websocket;
+
+            # Not a WebSocket
+            return;
+        }
+    );
+
+    return $self;
+}
+
+sub add_child {
+    my ($self, $route) = @_;
+
+    # We are the parent
+    $route->parent($self);
+    weaken $route->{parent};
+
+    # Add to tree
+    push @{$self->children}, $route;
+
+    return $self;
+}
+
+sub add_condition {
+    my ($self, $name, $condition) = @_;
+
+    # Add
+    $self->dictionary->{$name} = $condition;
+
+    return $self;
+}
+
+sub any { shift->_generate_route(ref $_[0] ? shift : [], @_) }
+
+# Hey. What kind of party is this? There's no booze and only one hooker.
+sub auto_render {
+    my ($self, $c) = @_;
+
+    # Transaction
+    my $tx = $c->tx;
+
+    # Rendering
+    my $success = eval {
+
+        # Render
+        $c->render unless $c->stash->{'mojo.rendered'} || $tx->is_websocket;
+
+        # Success
+        1;
+    };
+
+    # Renderer error
+    $c->render_exception($@) if !$success && $@;
+
+    # Rendered
+    return;
+}
+
+sub bridge { shift->route(@_)->inline(1) }
+
+sub detour {
+    my $self = shift;
+
+    # Partial
+    $self->partial('path');
+
+    # Defaults
+    $self->to(@_);
+
+    return $self;
+}
+
+sub dispatch {
+    my ($self, $c) = @_;
+
+    # Response
+    my $res = $c->res;
+
+    # Already rendered
+    return if $res->code;
+
+    # Path
+    my $path = $c->stash->{path};
+    $path = "/$path" if defined $path && $path !~ /^\//;
+
+    # Match
+    my $m = Mojolicious::Routes::Match->new($c, $path);
+    $m->match($self);
+    $c->match($m);
+
+    # No match
+    return 1 unless $m && @{$m->stack};
+
+    # Status
+    unless ($res->code) {
+
+        # Websocket handshake
+        $res->code(101) if !$res->code && $c->tx->is_websocket;
+
+        # Error or 200
+        my ($error, $code) = $c->req->error;
+        $res->code($code) if $code;
+    }
+
+    # Walk the stack
+    return 1 if $self->_walk_stack($c);
+
+    # Render
+    return $self->auto_render($c);
+}
+
+sub get { shift->_generate_route('get', @_) }
+
+sub hide { push @{shift->hidden}, @_ }
+
+sub is_endpoint {
+    my $self = shift;
+    return   if $self->inline;
+    return 1 if $self->block;
+    return   if @{$self->children};
+    return 1;
+}
+
+sub is_websocket {
+    my $self = shift;
+    return 1 if $self->{_websocket};
+    if (my $parent = $self->parent) { return $parent->is_websocket }
+    return;
+}
+
+# Dr. Zoidberg, can you note the time and declare the patient legally dead?
+# Can I! That’s my specialty!
+sub name {
+    my ($self, $name) = @_;
+
+    # New name
+    if (defined $name) {
+
+        # Generate
+        if ($name eq '*') {
+            $name = $self->pattern->pattern;
+            $name =~ s/\W+//g;
+        }
+        $self->{_name} = $name;
+
+        return $self;
+    }
+
+    return $self->{_name};
+}
+
+sub over {
+    my $self = shift;
+
+    # Shortcut
+    return $self unless @_;
+
+    # Conditions
+    my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
+    push @{$self->conditions}, @$conditions;
+
+    return $self;
+}
+
+sub parse {
+    my $self = shift;
+
+    # Pattern does the real work
+    $self->pattern->parse(@_);
+
+    return $self;
+}
+
+sub post { shift->_generate_route('post', @_) }
+
+sub render {
+    my ($self, $path, $values) = @_;
+
+    # Path prefix
+    my $prefix = $self->pattern->render($values);
+    $path = $prefix . $path unless $prefix eq '/';
+
+    # Make sure there is always a root
+    $path = '/' if !$path && !$self->parent;
+
+    # Format
+    if ((my $format = $values->{format}) && !$self->parent) {
+        $path .= ".$format" unless $path =~ /\.[^\/]+$/;
+    }
+
+    # Parent
+    $path = $self->parent->render($path, $values) if $self->parent;
+
+    return $path;
+}
+
+# Morbo forget how you spell that letter that looks like a man wearing a hat.
+# Hello, tiny man. I will destroy you!
+sub route {
+    my $self = shift;
+
+    # New route
+    my $route = $self->new(@_);
+    $self->add_child($route);
+
+    return $route;
+}
+
+sub to {
+    my $self = shift;
+
+    # Shortcut
+    return $self unless @_;
+
+    # Single argument
+    my ($shortcut, $defaults);
+    if (@_ == 1) {
+
+        # Hash
+        $defaults = shift if ref $_[0] eq 'HASH';
+        $shortcut = shift if $_[0];
+    }
+
+    # Multiple arguments
+    else {
+
+        # Odd
+        if (@_ % 2) {
+            $shortcut = shift;
+            $defaults = {@_};
+        }
+
+        # Even
+        else {
+
+            # Shortcut and defaults
+            if (ref $_[1] eq 'HASH') {
+                $shortcut = shift;
+                $defaults = shift;
+            }
+
+            # Just defaults
+            else { $defaults = {@_} }
+        }
+    }
+
+    # Shortcut
+    if ($shortcut) {
+
+        # App
+        if (ref $shortcut || $shortcut =~ /^[\w\:]+$/) {
+            $defaults->{app} = $shortcut;
+        }
+
+        # Controller and action
+        elsif ($shortcut =~ /^([\w\-]+)?\#(\w+)?$/) {
+            $defaults->{controller} = $1 if defined $1;
+            $defaults->{action}     = $2 if defined $2;
+        }
+    }
+
+    # Pattern
+    my $pattern = $self->pattern;
+
+    # Defaults
+    my $old = $pattern->defaults;
+    $pattern->defaults({%$old, %$defaults}) if $defaults;
+
+    return $self;
+}
+
+sub to_string {
+    my $self = shift;
+    my $pattern = $self->parent ? $self->parent->to_string : '';
+    $pattern .= $self->pattern->pattern if $self->pattern->pattern;
+    return $pattern;
+}
+
+sub under { shift->_generate_route('under', @_) }
+
+sub via {
+    my $self = shift;
+
+    # Methods
+    my $methods = ref $_[0] ? $_[0] : [@_];
+
+    # Shortcut
+    return $self unless @$methods;
+
+    # Condition
+    push @{$self->conditions}, method => [map { lc $_ } @$methods];
+
+    return $self;
+}
+
+sub waypoint { shift->route(@_)->block(1) }
+
+sub websocket {
+    my $self = shift;
+
+    # Route
+    my $route = $self->any(@_);
+
+    # Condition
+    push @{$route->conditions}, websocket => 1;
+    $route->{_websocket} = 1;
+
+    return $route;
+}
+
+sub _dispatch_callback {
+    my ($self, $c, $staging) = @_;
+
+    # Debug
+    $c->app->log->debug(qq/Dispatching callback./);
+
+    # Dispatch
+    my $continue;
+    my $cb      = $c->match->captures->{cb};
+    my $success = eval {
+
+        # Callback
+        $continue = $cb->($c);
+
+        # Success
+        1;
+    };
+
+    # Callback error
+    if (!$success && $@) {
+        my $e = Mojo::Exception->new($@);
+        $c->app->log->error($e);
+        return $e;
+    }
+
+    # Success!
+    return 1 unless $staging;
+    return 1 if $continue;
+
+    return;
+}
+
+sub _dispatch_controller {
+    my ($self, $c, $staging) = @_;
+
+    # Application
+    my $app = $c->match->captures->{app};
+
+    # Class
+    $app ||= $self->_generate_class($c);
+    return 1 unless $app;
+
+    # Method
+    my $method = $self->_generate_method($c);
+
+    # Debug
+    my $dispatch = ref $app || $app;
+    $dispatch .= "->$method" if $method;
+    $c->app->log->debug("Dispatching $dispatch.");
+
+    # Load class
+    unless (ref $app && $self->{_loaded}->{$app}) {
+
+        # Load
+        if (my $e = Mojo::Loader->load($app)) {
+
+            # Doesn't exist
+            unless (ref $e) {
+                $c->app->log->debug("$app does not exist, maybe a typo?");
+                return;
+            }
+
+            # Error
+            $c->app->log->error($e);
+            return $e;
+        }
+
+        # Loaded
+        $self->{_loaded}->{$app}++;
+    }
+
+    # Dispatch
+    my $continue;
+    my $success = eval {
+
+        # Instantiate
+        $app = $app->new($c) unless ref $app;
+
+        # Action
+        if ($method && $app->isa($self->controller_base_class)) {
+
+            # Call action
+            $continue = $app->$method if $app->can($method);
+
+            # Merge stash
+            my $new = $app->stash;
+            @{$c->stash}{keys %$new} = values %$new;
+        }
+
+        # Handler
+        elsif ($app->isa('Mojo')) {
+
+            # Connect routes
+            if ($app->can('routes')) {
+                my $r = $app->routes;
+                unless ($r->parent) {
+                    $r->parent($c->match->endpoint);
+                    weaken $r->{parent};
+                }
+            }
+
+            # Handler
+            $app->handler($c);
+        }
+
+        # Success
+        1;
+    };
+
+    # Controller error
+    if (!$success && $@) {
+        my $e = Mojo::Exception->new($@);
+        $c->app->log->error($e);
+        return $e;
+    }
+
+    # Success!
+    return 1 unless $staging;
+    return 1 if $continue;
+
+    return;
+}
+
+sub _generate_class {
+    my ($self, $c) = @_;
+
+    # Field
+    my $field = $c->match->captures;
+
+    # Class
+    my $class = $field->{class};
+    my $controller = $field->{controller} || '';
+    unless ($class) {
+        $class = $controller;
+        camelize $class;
+    }
+
+    # Namespace
+    my $namespace = $field->{namespace};
+    $namespace = $self->namespace unless defined $namespace;
+    $class = length $class ? "${namespace}::$class" : $namespace
+      if length $namespace;
+
+    # Invalid
+    return unless $class =~ /^[a-zA-Z0-9_:]+$/;
+
+    return $class;
+}
+
+sub _generate_method {
+    my ($self, $c) = @_;
+
+    # Field
+    my $field = $c->match->captures;
+
+    # Prepare hidden
+    unless ($self->{_hidden}) {
+        $self->{_hidden} = {};
+        $self->{_hidden}->{$_}++ for @{$self->hidden};
+    }
+
+    my $method = $field->{method};
+    $method ||= $field->{action};
+
+    # Shortcut
+    return unless $method;
+
+    # Shortcut for hidden methods
+    if ($self->{_hidden}->{$method} || index($method, '_') == 0) {
+        $c->app->log->debug(qq/Action "$method" is not allowed./);
+        return;
+    }
+
+    # Invalid
+    unless ($method =~ /^[a-zA-Z0-9_:]+$/) {
+        $c->app->log->debug(qq/Action "$method" is invalid./);
+        return;
+    }
+
+    return $method;
+}
+
+sub _generate_route {
+    my ($self, $methods, @args) = @_;
+
+    my ($cb, $constraints, $defaults, $name, $pattern);
+    my $conditions = [];
+
+    # Route information
+    while (defined(my $arg = shift @args)) {
+
+        # First scalar is the pattern
+        if (!ref $arg && !$pattern) { $pattern = $arg }
+
+        # Scalar
+        elsif (!ref $arg && @args) {
+            push @$conditions, $arg, shift @args;
+        }
+
+        # Last scalar is the route name
+        elsif (!ref $arg) { $name = $arg }
+
+        # Callback
+        elsif (ref $arg eq 'CODE') { $cb = $arg }
+
+        # Constraints
+        elsif (ref $arg eq 'ARRAY') { $constraints = $arg }
+
+        # Defaults
+        elsif (ref $arg eq 'HASH') { $defaults = $arg }
+    }
+
+    # Defaults
+    $constraints ||= [];
+
+    # Defaults
+    $defaults ||= {};
+    $defaults->{cb} = $cb if $cb;
+
+    # Name
+    $name ||= '';
+
+    # Create bridge
+    return $self->bridge($pattern, {@$constraints})->over($conditions)
+      ->to($defaults)->name($name)
+      if !ref $methods && $methods eq 'under';
+
+    # Create route
+    my $route =
+      $self->route($pattern, {@$constraints})->over($conditions)
+      ->via($methods)->to($defaults)->name($name);
+
+    return $route;
+}
+
+sub _walk_stack {
+    my ($self, $c) = @_;
+
+    # Stack
+    my $stack = $c->match->stack;
+
+    # Walk the stack
+    my $staging = @$stack;
+    for my $field (@$stack) {
+        $staging--;
+
+        # Stash
+        my $stash = $c->stash;
+
+        # Captures
+        my $captures = $stash->{'mojo.captures'} ||= {};
+        $stash->{'mojo.captures'} = {%$captures, %$field};
+
+        # Merge in captures
+        @{$c->stash}{keys %$field} = values %$field;
+
+        # Captures
+        $c->match->captures($field);
+
+        # Dispatch
+        my $e =
+            $field->{cb}
+          ? $self->_dispatch_callback($c, $staging)
+          : $self->_dispatch_controller($c, $staging);
+
+        # Exception
+        if (ref $e) {
+            $c->render_exception($e);
+            return 1;
+        }
+
+        # Break the chain
+        return 1 if $staging && !$e;
+    }
+
+    # Done
+    return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Routes - Always Find Your Destination With Routes
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Routes;
+
+    # New routes tree
+    my $r = Mojolicious::Routes->new;
+
+    # Normal route matching "/articles" with parameters "controller" and
+    # "action"
+    $r->route('/articles')->to(controller => 'article', action => 'list');
+
+    # Route with a placeholder matching everything but "/" and "."
+    $r->route('/:controller')->to(action => 'list');
+
+    # Route with a placeholder and regex constraint
+    $r->route('/articles/:id', id => qr/\d+/)
+      ->to(controller => 'article', action => 'view');
+
+    # Route with an optional parameter "year"
+    $r->route('/archive/:year')
+      ->to(controller => 'archive', action => 'list', year => undef);
+
+    # Nested route for two actions sharing the same "controller" parameter
+    my $books = $r->route('/books/:id')->to(controller => 'book');
+    $books->route('/edit')->to(action => 'edit');
+    $books->route('/delete')->to(action => 'delete');
+
+    # Bridges can be used to chain multiple routes
+    $r->bridge->to(controller => 'foo', action =>'auth')
+      ->route('/blog')->to(action => 'list');
+
+    # Waypoints are similar to bridges and nested routes but can also match
+    # if they are not the actual endpoint of the whole route
+    my $b = $r->waypoint('/books')->to(controller => 'books', action => 'list');
+    $b->route('/:id', id => qr/\d+/)->to(action => 'view');
+
+    # Simplified Mojolicious::Lite style route generation is also possible
+    $r->get('/')->to(controller => 'blog', action => 'welcome');
+    my $blog = $r->under('/blog');
+    $blog->post('/list')->to('blog#list');
+    $blog->get(sub { shift->render(text => 'Go away!') });
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Routes> is a very powerful implementation of the famous routes
+pattern and the core of the L<Mojolicious> web framework.
+See L<Mojolicious::Guide::Routing> for more.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Routes> implements the following attributes.
+
+=head2 C<block>
+
+    my $block = $r->block;
+    $r        = $r->block(1);
+
+Allow this route to match even if it's not an endpoint, used for waypoints.
+
+=head2 C<children>
+
+    my $children = $r->children;
+    $r           = $r->children([Mojolicious::Routes->new]);
+
+The children of this routes object, used for nesting routes.
+
+=head2 C<conditions>
+
+    my $conditions  = $r->conditions;
+    $r              = $r->conditions([foo => qr/\w+/]);
+
+Contains condition parameters for this route, used for C<over>.
+
+=head2 C<controller_base_class>
+
+    my $base = $r->controller_base_class;
+    $r       = $r->controller_base_class('Mojolicious::Controller');
+
+Base class used to identify controllers, defaults to
+L<Mojolicious::Controller>.
+
+=head2 C<dictionary>
+
+    my $dictionary = $r->dictionary;
+    $r             = $r->dictionary({foo => sub { ... }});
+
+Contains all available conditions for this route.
+There are currently two conditions built in, C<method> and C<websocket>.
+
+=head2 C<hidden>
+
+    my $hidden = $r->hidden;
+    $r         = $r->hidden([qw/new attr tx render req res stash/]);
+
+Controller methods and attributes that are hidden from routes.
+
+=head2 C<inline>
+
+    my $inline = $r->inline;
+    $r         = $r->inline(1);
+
+Allow C<bridge> semantics for this route.
+
+=head2 C<namespace>
+
+    my $namespace = $r->namespace;
+    $r            = $r->namespace('Foo::Bar::Controller');
+
+Namespace to search for controllers.
+
+=head2 C<parent>
+
+    my $parent = $r->parent;
+    $r         = $r->parent(Mojolicious::Routes->new);
+
+The parent of this route, used for nesting routes.
+
+=head2 C<partial>
+
+    my $partial = $r->partial;
+    $r          = $r->partial('path');
+
+Route has no specific end, remaining characters will be captured with the
+partial name.
+Note that this attribute is EXPERIMENTAL and might change without warning!
+
+=head2 C<pattern>
+
+    my $pattern = $r->pattern;
+    $r          = $r->pattern(Mojolicious::Routes::Pattern->new);
+
+Pattern for this route, by default a L<Mojolicious::Routes::Pattern> object
+and used for matching.
+
+=head1 METHODS
+
+L<Mojolicious::Routes> inherits all methods from L<Mojo::Base> and implements
+the following ones.
+
+=head2 C<new>
+
+    my $r = Mojolicious::Routes->new;
+    my $r = Mojolicious::Routes->new('/:controller/:action');
+
+Construct a new route object.
+
+=head2 C<add_child>
+
+    $r = $r->add_child(Mojolicious::Route->new);
+
+Add a new child to this route.
+
+=head2 C<add_condition>
+
+    $r = $r->add_condition(foo => sub { ... });
+
+Add a new condition for this route.
+
+=head2 C<any>
+
+    my $any = $route->any('/:foo' => sub {...});
+    my $any = $route->any([qw/get post/] => '/:foo' => sub {...});
+
+Generate route matching any of the listed HTTP request methods or all.
+See also the L<Mojolicious::Lite> tutorial for more argument variations.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 C<auto_render>
+
+    $r->auto_render(Mojolicious::Controller->new);
+
+Automatic rendering.
+
+=head2 C<bridge>
+
+    my $bridge = $r->bridge;
+    my $bridge = $r->bridge('/:controller/:action');
+
+Add a new bridge to this route as a nested child.
+
+=head2 C<detour>
+
+    $r = $r->detour(action => 'foo');
+    $r = $r->detour({action => 'foo'});
+    $r = $r->detour('controller#action');
+    $r = $r->detour('controller#action', foo => 'bar');
+    $r = $r->detour('controller#action', {foo => 'bar'});
+    $r = $r->detour($app);
+    $r = $r->detour($app, foo => 'bar');
+    $r = $r->detour($app, {foo => 'bar'});
+    $r = $r->detour('MyApp');
+    $r = $r->detour('MyApp', foo => 'bar');
+    $r = $r->detour('MyApp', {foo => 'bar'});
+
+Set default parameters for this route and allow partial matching to simplify
+application embedding.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 C<dispatch>
+
+    my $e = $r->dispatch(Mojolicious::Controller->new);
+
+Match routes and dispatch.
+
+=head2 C<get>
+
+    my $get = $route->get('/:foo' => sub {...});
+
+Generate route matching only C<GET> requests.
+See also the L<Mojolicious::Lite> tutorial for more argument variations.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 C<hide>
+
+    $r = $r->hide('new');
+
+Hide controller method or attribute from routes.
+
+=head2 C<is_endpoint>
+
+    my $is_endpoint = $r->is_endpoint;
+
+Returns true if this route qualifies as an endpoint.
+
+=head2 C<is_websocket>
+
+    my $is_websocket = $r->is_websocket;
+
+Returns true if this route leads to a WebSocket.
+
+=head2 C<name>
+
+    my $name = $r->name;
+    $r       = $r->name('foo');
+    $r       = $r->name('*');
+
+The name of this route, the special value C<*> will generate a name based on
+the route pattern.
+Note that the name C<current> is reserved for refering to the current route.
+
+=head2 C<over>
+
+    $r = $r->over(foo => qr/\w+/);
+
+Apply condition parameters to this route.
+
+=head2 C<parse>
+
+    $r = $r->parse('/:controller/:action');
+
+Parse a pattern.
+
+=head2 C<post>
+
+    my $post = $route->post('/:foo' => sub {...});
+
+Generate route matching only C<POST> requests.
+See also the L<Mojolicious::Lite> tutorial for more argument variations.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 C<render>
+
+    my $path = $r->render($path);
+    my $path = $r->render($path, {foo => 'bar'});
+
+Render route with parameters into a path.
+
+=head2 C<route>
+
+    my $route = $r->route('/:c/:a', a => qr/\w+/);
+
+Add a new nested child to this route.
+
+=head2 C<to>
+
+    my $to  = $r->to;
+    $r = $r->to(action => 'foo');
+    $r = $r->to({action => 'foo'});
+    $r = $r->to('controller#action');
+    $r = $r->to('controller#action', foo => 'bar');
+    $r = $r->to('controller#action', {foo => 'bar'});
+    $r = $r->to($app);
+    $r = $r->to($app, foo => 'bar');
+    $r = $r->to($app, {foo => 'bar'});
+    $r = $r->to('MyApp');
+    $r = $r->to('MyApp', foo => 'bar');
+    $r = $r->to('MyApp', {foo => 'bar'});
+
+Set default parameters for this route.
+
+=head2 C<to_string>
+
+    my $string = $r->to_string;
+
+Stringifies the whole route.
+
+=head2 C<under>
+
+    my $under = $route->under(sub {...});
+    my $under = $route->under('/:foo');
+
+Generate bridges.
+See also the L<Mojolicious::Lite> tutorial for more argument variations.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head2 C<via>
+
+    $r = $r->via('get');
+    $r = $r->via(qw/get post/);
+    $r = $r->via([qw/get post/]);
+
+Apply C<method> constraint to this route.
+
+=head2 C<waypoint>
+
+    my $route = $r->waypoint('/:c/:a', a => qr/\w+/);
+
+Add a waypoint to this route as nested child.
+
+=head2 C<websocket>
+
+    my $websocket = $route->websocket('/:foo' => sub {...});
+
+Generate route matching only C<WebSocket> handshakes.
+See also the L<Mojolicious::Lite> tutorial for more argument variations.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,156 @@
+package Mojolicious::Session;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use Mojo::Util qw/b64_decode b64_encode/;
+use Storable qw/freeze thaw/;
+
+__PACKAGE__->attr('cookie_domain');
+__PACKAGE__->attr(cookie_name        => 'mojolicious');
+__PACKAGE__->attr(cookie_path        => '/');
+__PACKAGE__->attr(default_expiration => 3600);
+
+# Bender, quit destroying the universe!
+sub load {
+    my ($self, $c) = @_;
+
+    # Session cookie
+    return unless my $value = $c->signed_cookie($self->cookie_name);
+
+    # Decode
+    b64_decode $value;
+
+    # Thaw
+    my $session = thaw $value;
+
+    # Expiration
+    return unless my $expires = delete $session->{expires};
+    return unless $expires > time;
+
+    # Content
+    my $stash = $c->stash;
+    return unless $stash->{'mojo.active_session'} = keys %$session;
+    $stash->{'mojo.session'} = $session;
+
+    # Flash
+    $session->{old_flash} = delete $session->{flash} if $session->{flash};
+}
+
+# Emotions are dumb and should be hated.
+sub store {
+    my ($self, $c) = @_;
+
+    # Session
+    my $stash = $c->stash;
+    return unless my $session = $stash->{'mojo.session'};
+    return unless keys %$session || $stash->{'mojo.active_session'};
+
+    # Flash
+    delete $session->{old_flash};
+    delete $session->{flash} unless keys %{$session->{flash}};
+
+    # Default to expiring session
+    my $expires = 1;
+    my $value   = '';
+
+    # Actual session data
+    my $default = delete $session->{expires};
+    if (keys %$session) {
+
+        # Expiration
+        $expires = $session->{expires} = $default
+          ||= time + $self->default_expiration;
+
+        # Freeze
+        $value = freeze $session;
+
+        # Encode
+        b64_encode $value, '';
+    }
+
+    # Options
+    my $options = {expires => $expires, path => $self->cookie_path};
+    my $domain = $self->cookie_domain;
+    $options->{domain} = $domain if $domain;
+
+    # Session cookie
+    $c->signed_cookie($self->cookie_name, $value, $options);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Session - Signed Cookie Based Sessions
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Session;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Session> is a very simple signed cookie based session
+implementation.
+All data gets stored on the client side, but is protected from unwanted
+changes with a signature.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Session> implements the following attributes.
+
+=head2 C<cookie_domain>
+
+    my $domain = $session->cookie_domain;
+    $session   = $session->cookie_domain('.example.com');
+
+Domain for session cookie, not defined by default.
+
+=head2 C<cookie_name>
+
+    my $name = $session->cookie_name;
+    $session = $session->cookie_name('session');
+
+Name of the signed cookie used to store session data, defaults to
+C<mojolicious>.
+
+=head2 C<cookie_path>
+
+    my $path = $session->cookie_path;
+    $session = $session->cookie_path('/foo');
+
+Path for session cookie, defaults to C</>.
+
+=head2 C<default_expiration>
+
+    my $time = $session->default_expiration;
+    $session = $session->default_expiration(3600);
+
+Time for the session to expire in seconds from now, defaults to C<3600>.
+The expiration timeout gets refreshed for every request.
+
+=head1 METHODS
+
+L<Mojolicious::Session> inherits all methods from L<Mojo::Base> and
+implements the following ones.
+
+=head2 C<load>
+
+    $session->load($c);
+
+Load session data from signed cookie.
+
+=head2 C<store>
+
+    $session->store($c);
+
+Store session data in signed cookie.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,348 @@
+package Mojolicious::Static;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+use File::stat;
+use File::Spec;
+use Mojo::Asset::File;
+use Mojo::Asset::Memory;
+use Mojo::Command;
+use Mojo::Content::Single;
+use Mojo::Path;
+
+__PACKAGE__->attr([qw/default_static_class prefix root/]);
+
+# Valentine's Day's coming? Aw crap! I forgot to get a girlfriend again!
+sub dispatch {
+    my ($self, $c) = @_;
+
+    # Already rendered
+    return if $c->res->code;
+
+    # Canonical path
+    my $path = $c->req->url->path->clone->canonicalize->to_string;
+
+    # Prefix
+    if (my $prefix = $self->prefix) {
+        return 1 unless $path =~ s/^$prefix//;
+    }
+
+    # Parts
+    my @parts = @{Mojo::Path->new->parse($path)->parts};
+
+    # Shortcut
+    return 1 unless @parts;
+
+    # Prevent directory traversal
+    return 1 if $parts[0] eq '..';
+
+    # Serve static file
+    unless ($self->serve($c, join('/', @parts))) {
+
+        # Resume
+        $c->tx->resume;
+
+        return;
+    }
+
+    return 1;
+}
+
+sub serve {
+    my ($self, $c, $rel) = @_;
+
+    # Append path to root
+    my $path = File::Spec->catfile($self->root, split('/', $rel));
+
+    # Extension
+    $path =~ /\.(\w+)$/;
+    my $ext = $1;
+
+    # Type
+    my $type = $c->app->types->type($ext) || 'text/plain';
+
+    # Response
+    my $res = $c->res;
+
+    # Asset
+    my $asset;
+
+    # Modified
+    my $modified = $self->{_modified} ||= time;
+
+    # Size
+    my $size = 0;
+
+    # File
+    if (-f $path) {
+
+        # Readable
+        if (-r $path) {
+
+            # Modified
+            my $stat = stat($path);
+            $modified = $stat->mtime;
+
+            # Size
+            $size = $stat->size;
+
+            # Content
+            $asset = Mojo::Asset::File->new(path => $path);
+        }
+
+        # Exists, but is forbidden
+        else {
+            $c->app->log->debug('File forbidden.');
+            $res->code(403) and return;
+        }
+    }
+
+    # Inline file
+    elsif (defined(my $file = $self->_get_inline_file($c, $rel))) {
+        $size  = length $file;
+        $asset = Mojo::Asset::Memory->new->add_chunk($file);
+    }
+
+    # Found
+    if ($asset) {
+
+        # Log
+        $c->app->log->debug(qq/Serving static file "$rel"./);
+
+        # Request
+        my $req = $c->req;
+
+        # Request headers
+        my $rqh = $req->headers;
+
+        # Response headers
+        my $rsh = $res->headers;
+
+        # If modified since
+        if (my $date = $rqh->if_modified_since) {
+
+            # Not modified
+            my $since = Mojo::Date->new($date)->epoch;
+            if (defined $since && $since == $modified) {
+                $c->app->log->debug('File not modified.');
+                $res->code(304);
+                $rsh->remove('Content-Type');
+                $rsh->remove('Content-Length');
+                $rsh->remove('Content-Disposition');
+                return;
+            }
+        }
+
+        # Start and end
+        my $start = 0;
+        my $end = $size - 1 >= 0 ? $size - 1 : 0;
+
+        # Range
+        if (my $range = $rqh->range) {
+            if ($range =~ m/^bytes=(\d+)\-(\d+)?/ && $1 <= $end) {
+                $start = $1;
+                $end = $2 if defined $2 && $2 <= $end;
+                $res->code(206);
+                $rsh->content_length($end - $start + 1);
+                $rsh->content_range("bytes $start-$end/$size");
+                $c->app->log->debug("Range request: $start-$end/$size.");
+            }
+            else {
+
+                # Not satisfiable
+                $res->code(416);
+                return;
+            }
+        }
+        $asset->start_range($start);
+        $asset->end_range($end);
+
+        # Response
+        $res->code(200) unless $res->code;
+        $res->content->asset($asset);
+        $rsh->content_type($type);
+        $rsh->accept_ranges('bytes');
+        $rsh->last_modified(Mojo::Date->new($modified));
+        return;
+    }
+
+    return 1;
+}
+
+sub serve_404 { shift->serve_error(shift, 404) }
+
+sub serve_500 { shift->serve_error(shift, 500) }
+
+sub serve_error {
+    my ($self, $c, $code, $rel) = @_;
+
+    # Shortcut
+    return 1 unless $c && $code;
+
+    my $res = $c->res;
+
+    # Render once
+    return if ($res->code || '') eq $code;
+
+    # Code
+    $res->code($code);
+
+    # Default to "code.html"
+    $rel ||= "$code.html";
+
+    # File
+    if (!$self->serve($c, $rel)) {
+
+        # Log
+        $c->app->log->debug(qq/Serving error file "$rel"./);
+    }
+
+    # 404
+    elsif ($code == 404) {
+
+        # Log
+        $c->app->log->debug('Serving 404 error.');
+
+        $res->headers->content_type('text/html');
+        $res->body(<<'EOF');
+<!doctype html><html>
+    <head><title>File Not Found</title></head>
+    <body><h2>File Not Found</h2></body>
+</html>
+EOF
+    }
+
+    # Error
+    else {
+
+        # Log
+        $c->app->log->debug(qq/Serving error "$code"./);
+
+        $res->headers->content_type('text/html');
+        $res->body(<<'EOF');
+<!doctype html><html>
+    <head><title>Internal Server Error</title></head>
+    <body><h2>Internal Server Error</h2></body>
+</html>
+EOF
+    }
+
+    return;
+}
+
+sub _get_inline_file {
+    my ($self, $c, $rel) = @_;
+
+    # Protect templates
+    return if $rel =~ /\.\w+\.\w+$/;
+
+    # Class
+    my $class =
+         $c->stash->{static_class}
+      || $ENV{MOJO_STATIC_CLASS}
+      || $self->default_static_class
+      || 'main';
+
+    # Inline files
+    my $inline = $self->{_inline_files}->{$class};
+    unless ($inline) {
+        my $files = Mojo::Command->new->get_all_data($class) || {};
+        $inline = $self->{_inline_files}->{$class} = [keys %$files];
+    }
+
+    # Find
+    for my $path (@$inline) {
+        return Mojo::Command->new->get_data($path, $class) if $path eq $rel;
+    }
+
+    # Nothing
+    return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Static - Serve Static Files
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Static;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Static> is a dispatcher for static files with C<Range> and
+C<If-Modified-Since> support.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Static> implements the following attributes.
+
+=head2 C<default_static_class>
+
+    my $class = $static->default_static_class;
+    $static   = $static->default_static_class('main');
+
+The dispatcher will use this class to look for files in the C<DATA> section.
+
+=head2 C<prefix>
+
+    my $prefix = $static->prefix;
+    $static    = $static->prefix('/static');
+
+Prefix path to remove from incoming paths before dispatching.
+
+=head2 C<root>
+
+    my $root = $static->root;
+    $static  = $static->root('/foo/bar/files');
+
+Directory to serve static files from.
+
+=head1 METHODS
+
+L<Mojolicious::Static> inherits all methods from L<Mojo::Base>
+and implements the following ones.
+
+=head2 C<dispatch>
+
+    my $success = $static->dispatch($c);
+
+Dispatch a L<Mojolicious::Controller> object.
+
+=head2 C<serve>
+
+    my $success = $static->serve($c, 'foo/bar.html');
+
+Serve a specific file.
+
+=head2 C<serve_404>
+
+    my $success = $static->serve_404($c);
+    my $success = $static->serve_404($c, '404.html');
+
+Serve a C<404> error page, guaranteed to render at least a default page.
+
+=head2 C<serve_500>
+
+    my $success = $static->serve_500($c);
+    my $success = $static->serve_500($c, '500.html');
+
+Serve a C<500> error page, guaranteed to render at least a default page.
+
+=head2 C<serve_error>
+
+    my $success = $static->serve_error($c, 404);
+    my $success = $static->serve_error($c, 404, '404.html');
+
+Serve error page, guaranteed to render at least a default page.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -0,0 +1,92 @@
+package Mojolicious::Types;
+
+use strict;
+use warnings;
+
+use base 'Mojo::Base';
+
+# Once again, the conservative, sandwich-heavy portfolio pays off for the
+# hungry investor.
+__PACKAGE__->attr(
+    types => sub {
+        return {
+            atom => 'application/atom+xml',
+            bin  => 'application/octet-stream',
+            css  => 'text/css',
+            gif  => 'image/gif',
+            gz   => 'application/gzip',
+            htm  => 'text/html',
+            html => 'text/html',
+            ico  => 'image/x-icon',
+            jpeg => 'image/jpeg',
+            jpg  => 'image/jpeg',
+            js   => 'application/x-javascript',
+            json => 'application/json',
+            mp3  => 'audio/mpeg',
+            png  => 'image/png',
+            rss  => 'application/rss+xml',
+            svg  => 'image/svg+xml',
+            tar  => 'application/x-tar',
+            txt  => 'text/plain',
+            xml  => 'text/xml',
+            zip  => 'application/zip'
+        };
+    }
+);
+
+# Magic. Got it.
+sub type {
+    my ($self, $ext, $type) = @_;
+
+    # Set
+    if ($type) {
+        $self->types->{$ext} = $type;
+        return $self;
+    }
+
+    return $self->types->{$ext || ''};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Mojolicious::Types - MIME Types
+
+=head1 SYNOPSIS
+
+    use Mojolicious::Types;
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Types> is a container for MIME types.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Types> implements the following attributes.
+
+=head2 C<types>
+
+    my $map = $types->types;
+    $types  = $types->types({png => 'image/png'});
+
+List of MIME types.
+
+=head1 METHODS
+
+L<Mojolicious::Types> inherits all methods from L<Mojo::Base> and implements the
+following ones.
+
+=head2 C<type>
+
+    my $type = $types->type('png');
+    $types   = $types->type(png => 'image/png');
+
+Get or set MIME type for file extension.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
@@ -8,17 +8,17 @@ use base 'Mojo';
 use Carp 'croak';
 use Mojolicious::Commands;
 use Mojolicious::Plugins;
-use MojoX::Dispatcher::Routes;
-use MojoX::Dispatcher::Static;
-use MojoX::Renderer;
-use MojoX::Session::Cookie;
-use MojoX::Types;
+use Mojolicious::Renderer;
+use Mojolicious::Routes;
+use Mojolicious::Session;
+use Mojolicious::Static;
+use Mojolicious::Types;
 
 __PACKAGE__->attr(controller_class => 'Mojolicious::Controller');
 __PACKAGE__->attr(mode => sub { ($ENV{MOJO_MODE} || 'development') });
 __PACKAGE__->attr(plugins  => sub { Mojolicious::Plugins->new });
-__PACKAGE__->attr(renderer => sub { MojoX::Renderer->new });
-__PACKAGE__->attr(routes   => sub { MojoX::Dispatcher::Routes->new });
+__PACKAGE__->attr(renderer => sub { Mojolicious::Renderer->new });
+__PACKAGE__->attr(routes   => sub { Mojolicious::Routes->new });
 __PACKAGE__->attr(
     secret => sub {
         my $self = shift;
@@ -30,14 +30,12 @@ __PACKAGE__->attr(
         return ref $self;
     }
 );
-__PACKAGE__->attr(session => sub { MojoX::Session::Cookie->new });
-__PACKAGE__->attr(static  => sub { MojoX::Dispatcher::Static->new });
-__PACKAGE__->attr(types   => sub { MojoX::Types->new });
+__PACKAGE__->attr(session => sub { Mojolicious::Session->new });
+__PACKAGE__->attr(static  => sub { Mojolicious::Static->new });
+__PACKAGE__->attr(types   => sub { Mojolicious::Types->new });
 
 our $CODENAME = 'Hot Beverage';
-our $VERSION  = '0.999936';
-
-our $AUTOLOAD;
+our $VERSION  = '0.999941';
 
 # These old doomsday devices are dangerously unstable.
 # I'll rest easier not knowing where they are.
@@ -45,7 +43,7 @@ sub AUTOLOAD {
     my $self = shift;
 
     # Method
-    my ($package, $method) = $AUTOLOAD =~ /^([\w\:]+)\:\:(\w+)$/;
+    my ($package, $method) = our $AUTOLOAD =~ /^([\w\:]+)\:\:(\w+)$/;
 
     # Helper
     croak qq/Can't locate object method "$method" via "$package"/
@@ -99,10 +97,6 @@ sub new {
     # Static
     my $static = $self->static;
 
-    # Types
-    $renderer->types($self->types);
-    $static->types($self->types);
-
     # Home
     my $home = $self->home;
 
@@ -362,8 +356,8 @@ magic and no requirements besides Perl 5.8.7.
 
 =item *
 
-Full stack HTTP 1.1 and WebSocket client/server implementation with IPv6,
-TLS, Bonjour, IDNA, Comet (long polling), chunking and multipart support.
+Full stack HTTP 1.1 and WebSocket client/server implementation with TLS,
+Bonjour, IDNA, Comet (long polling), chunking and multipart support.
 
 =item *
 
@@ -426,9 +420,6 @@ Web development for humans, making hard things possible and everything fun.
         The time is <%= $hour %>:<%= $minute %>:<%= $second %>.
     <% end %>
 
-For more user friendly documentation see L<Mojolicious::Guides> and
-L<Mojolicious::Lite>.
-
 =head2 Have Some Cake
 
 Loosely coupled building blocks, use what you like and just ignore the rest.
@@ -449,11 +440,61 @@ Loosely coupled building blocks, use what you like and just ignore the rest.
     |  CGI  | |  FastCGI  | |  PSGI  | |  HTTP 1.1  | |  WebSocket  |
     '-------' '-----------' '--------' '------------' '-------------'
 
-=head2 Installation
+=head2 Highlights
+
+These are some of the most important building blocks of L<Mojolicious>.
+
+=over 4
+
+=item L<Mojolicious::Lite>
+
+Micro Web Framework built on top of L<Mojolicious> for prototypes and small
+applications.
+
+=item L<Mojo::Client>
+
+Full featured async io HTTP 1.1 and WebSocket client.
+
+=item L<Mojo::DOM>
+
+Very fun and minimalistic XML/HTML5 DOM parser with CSS3 selector support.
+
+=item L<Mojo::JSON>
+
+Minimalistic JSON implementation that just works.
+
+=item L<Mojo::Server::Daemon>
+
+Full featured async io HTTP 1.1 and WebSocket server.
+
+=item L<Mojo::Server::CGI>, L<Mojo::Server::FastCGI>, L<Mojo::Server::PSGI>
 
-All you need is a oneliner.
+Transparent CGI, FastCGI and PSGI support out of the box.
 
-    curl -L cpanmin.us | perl - http://latest.mojolicio.us
+=item L<Mojo::Template>
+
+Very perlish and minimalistic template system.
+
+=item L<Mojo::ByteStream>
+
+Countless portable and very convenient bytestream manipulation methods.
+
+=item L<Mojolicious::Commands>
+
+Pluggable command line system and the backbone of the C<mojo> script.
+
+=item L<Test::Mojo>
+
+Test driven development toolkit for web applications.
+
+=item L<ojo>
+
+Fun oneliners using everything above.
+
+=back
+
+For more documentation see L<Mojolicious::Guides> and the tutorial in
+L<Mojolicious::Lite>!
 
 =head1 ATTRIBUTES
 
@@ -502,19 +543,19 @@ write a plugin.
 =head2 C<renderer>
 
     my $renderer = $app->renderer;
-    $app         = $app->renderer(MojoX::Renderer->new);
+    $app         = $app->renderer(Mojolicious::Renderer->new);
 
-Used in your application to render content, by default a L<MojoX::Renderer>
-object.
+Used in your application to render content, by default a
+L<Mojolicious::Renderer> object.
 The two main renderer plugins L<Mojolicious::Plugin::EpRenderer> and
 L<Mojolicious::Plugin::EplRenderer> contain more specific information.
 
 =head2 C<routes>
 
     my $routes = $app->routes;
-    $app       = $app->routes(MojoX::Dispatcher::Routes->new);
+    $app       = $app->routes(Mojolicious::Routes->new);
 
-The routes dispatcher, by default a L<MojoX::Dispatcher::Routes> object.
+The routes dispatcher, by default a L<Mojolicious::Routes> object.
 You use this in your startup method to define the url endpoints for your
 application.
 
@@ -535,21 +576,29 @@ application name which is not very secure, so you should change it!!!
 As long as you are using the unsecure default there will be debug messages in
 the log file reminding you to change your passphrase.
 
+=head2 C<session>
+
+    my $session = $app->session;
+    $app        = $app->session(Mojolicious::Session->new);
+
+Simple singed cookie based sessions, by default a L<Mojolicious::Session>
+object.
+
 =head2 C<static>
 
     my $static = $app->static;
-    $app       = $app->static(MojoX::Dispatcher::Static->new);
+    $app       = $app->static(Mojolicious::Static->new);
 
 For serving static assets from your C<public> directory, by default a
-L<MojoX::Dispatcher::Static> object.
+L<Mojolicious::Static> object.
 
 =head2 C<types>
 
     my $types = $app->types;
-    $app      = $app->types(MojoX::Types->new);
+    $app      = $app->types(Mojolicious::Types->new);
 
 Responsible for tracking the types of content you want to serve in your
-application, by default a L<MojoX::Types> object.
+application, by default a L<Mojolicious::Types> object.
 You can easily register new types.
 
     $app->types->type(vti => 'help/vampire');
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 43;
 
@@ -3,10 +3,10 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
-use Test::More tests => 47;
+use Test::More tests => 48;
 
 use_ok 'Mojo::Client';
 
@@ -83,9 +83,10 @@ is $tx->res->body, 'works', 'right content';
 
 # GET / (missing Content-Lengt header)
 $tx = $client->get("http://localhost:$port2/");
-ok $tx->success,    'successful';
+ok !$tx->success, 'not successful';
+is $tx->error, 'Interrupted, maybe a timeout?', 'right error';
 is $tx->kept_alive, undef, 'kept connection not alive';
-is $tx->keep_alive, 0, 'keep connection not alive';
+is $tx->keep_alive, 0,     'keep connection not alive';
 is $tx->res->code, 200,          'right status';
 is $tx->res->body, 'works too!', 'no content';
 
@@ -3,14 +3,14 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_TLS} = 1 }
+# Disable epoll, kqueue and TLS
+BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_TLS} = 1 }
 
 use Test::More;
 
-plan skip_all => 'set TEST_CLIENT to enable this test (developer only!)'
-  unless $ENV{TEST_CLIENT};
-plan tests => 102;
+plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
+  unless $ENV{TEST_ONLINE};
+plan tests => 101;
 
 # So then I said to the cop, "No, you're driving under the influence...
 # of being a jerk".
@@ -75,19 +75,6 @@ $async->get(
 $async->ioloop->start;
 is $kept_alive, 1, 'connection was kept alive';
 
-# Resolve TXT record
-my $record;
-$async->ioloop->resolve(
-    'google.com',
-    'TXT',
-    sub {
-        my ($self, $records) = @_;
-        $record = $records->[0];
-        $self->stop;
-    }
-)->start;
-like $record, qr/spf/, 'right record';
-
 # Nested keep alive
 my @kept_alive;
 $client->async->get(
@@ -0,0 +1,131 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
+
+use Test::More;
+plan skip_all => 'Perl 5.12 required for this test!'
+  unless eval 'use 5.12.0; 1';
+plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
+  unless $ENV{TEST_ONLINE};
+plan tests => 9;
+
+use_ok 'Mojo::IOLoop';
+
+use List::Util 'first';
+use Mojo::URL;
+
+# Your guilty consciences may make you vote Democratic, but secretly you all
+# yearn for a Republican president to lower taxes, brutalize criminals, and
+# rule you like a king!
+my $loop = Mojo::IOLoop->new;
+
+# Resolve TXT record
+my $result;
+$loop->resolve(
+    'google.com',
+    'TXT',
+    sub {
+        my ($self, $records) = @_;
+        $result = (first { $_->[0] eq 'TXT' } @$records)->[1];
+        $self->stop;
+    }
+)->start;
+like $result, qr/spf/, 'right record';
+
+# Resolve NS records
+my $found = 0;
+$loop->resolve(
+    'gmail.com',
+    'NS',
+    sub {
+        my ($self, $records) = @_;
+        $found++ if first { $_->[1] =~ /ns\d*.google\.com/ } @$records;
+        $self->stop;
+    }
+)->start;
+ok $found, 'found NS records';
+
+# Resolve AAAA record
+$result = undef;
+$loop->resolve(
+    'ipv6.google.com',
+    'AAAA',
+    sub {
+        my ($self, $records) = @_;
+        $result = (first { $_->[0] eq 'AAAA' } @$records)->[1];
+        $self->stop;
+    }
+)->start;
+like $result, $Mojo::URL::IPV6_RE, 'valid IPv6 record';
+
+# Resolve CNAME record
+$result = undef;
+$loop->resolve(
+    'ipv6.google.com',
+    'CNAME',
+    sub {
+        my ($self, $records) = @_;
+        $result = (first { $_->[0] eq 'CNAME' } @$records)->[1];
+        $self->stop;
+    }
+)->start;
+is $result, 'ipv6.l.google.com', 'right CNAME record';
+
+# Resolve MX records
+$found = 0;
+$loop->resolve(
+    'gmail.com',
+    'MX',
+    sub {
+        my ($self, $records) = @_;
+        $found++
+          if first { $_->[1] =~ /gmail-smtp-in\.l\.google\.com/ } @$records;
+        $self->stop;
+    }
+)->start;
+ok $found, 'found MX records';
+
+# Resolve A record and perform PTR roundtrip
+my ($a1, $ptr, $a2);
+$loop->resolve(
+    'perl.org',
+    'A',
+    sub {
+        my ($self, $records) = @_;
+        $a1 = (first { $_->[0] eq 'A' } @$records)->[1];
+        $self->resolve(
+            $a1, 'PTR',
+            sub {
+                my ($self, $records) = @_;
+                $ptr = $records->[0]->[1];
+                $self->resolve(
+                    $ptr, 'A',
+                    sub {
+                        my ($self, $records) = @_;
+                        $a2 = (first { $_->[0] eq 'A' } @$records)->[1];
+                        $self->stop;
+                    }
+                );
+            }
+        );
+    }
+)->start;
+like $a1, $Mojo::URL::IPV4_RE, 'valid IPv4 record';
+is $a1, $a2, 'PTR roundtrip succeeded';
+
+# Resolve PTR record (IPv6)
+$found = 0;
+$loop->resolve(
+    '2001:4f8:0:2:0:0:0:e',
+    'PTR',
+    sub {
+        my ($self, $records) = @_;
+        $found++ if first { $_->[1] eq 'freebsd.isc.org' } @$records;
+        $self->stop;
+    }
+)->start;
+ok $found, 'found IPv6 PTR record';
@@ -0,0 +1,44 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
+
+use Test::More;
+use Mojo::IOLoop;
+plan skip_all => 'IO::Socket::SSL 1.34 required for this test!'
+  unless Mojo::IOLoop::TLS;
+plan tests => 2;
+
+# To the panic room!
+# We don't have a panic room.
+# To the panic room store!
+my $loop   = Mojo::IOLoop->new;
+my $port   = Mojo::IOLoop->generate_port;
+my $server = '';
+my $client = '';
+$loop->listen(
+    port      => $port,
+    tls       => 1,
+    on_accept => sub {
+        shift->write(shift, 'test', sub { shift->write(shift, '321') });
+    },
+    on_read => sub { $server .= pop },
+    on_hup  => sub { $server .= 'hup' }
+);
+my $c = $loop->connect(
+    address    => 'localhost',
+    port       => $port,
+    tls        => 1,
+    on_connect => sub {
+        shift->write(shift, 'tset', sub { shift->write(shift, '123') });
+    },
+    on_read => sub { $client .= pop },
+    on_hup => sub { shift->stop }
+);
+$loop->connection_timeout($c => '0.5');
+$loop->start;
+is $server, 'tset123hup', 'right content';
+is $client, 'test321',    'right content';
@@ -60,8 +60,8 @@ ok LoaderTest::A->can('new'), 'loaded successfully';
 ok LoaderTest::B->can('new'), 'loaded successfully';
 ok LoaderTest::C->can('new'), 'loaded successfully';
 
-# Load unrelated class
-ok $loader->load('LoaderTest'), 'loaded successfully';
+# Class does not exist
+ok $loader->load('LoaderTest'), 'nothing to load';
 
 # Reload
 my $file = IO::File->new;
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 951;
+use Test::More tests => 952;
 
 use File::Spec;
 use File::Temp;
@@ -277,7 +277,7 @@ is $req->content->asset->slurp, 'abcdabcdefghi', 'right content';
 # Parse HTTP 1.1 chunked request with callback
 $req = Mojo::Message::Request->new;
 my $buffer = '';
-$req->on_read(sub { $buffer .= pop });
+$req->body(sub { $buffer .= pop });
 $req->parse("POST /foo/bar/baz.html?foo=13#23 HTTP/1.1\x0d\x0a");
 $req->parse("Content-Type: text/plain\x0d\x0a");
 $req->parse("Transfer-Encoding: chunked\x0d\x0a\x0d\x0a");
@@ -2117,7 +2117,8 @@ is $req->method,  'GET', 'right method';
 is $req->version, '1.1', 'right version';
 is $req->at_least_version('1.0'), 1,     'at least version 1.0';
 is $req->at_least_version('1.2'), undef, 'not version 1.2';
-is $req->url, '/perldoc?Mojo::Message::Request', 'right URL';
+is $req->url, '/perldoc?Mojo%3A%3AMessage%3A%3ARequest', 'right URL';
+is $req->url->query->params->[0], 'Mojo::Message::Request', 'right value';
 
 # Body helper
 $req = Mojo::Message::Request->new;
@@ -2138,8 +2139,8 @@ is $req->body, 0, 'right content';
 $req->body(sub { });
 is ref $req->body, 'CODE', 'body is callback';
 $req->body('hello!');
-is $req->body,    'hello!', 'right content';
-is $req->on_read, undef,    'no read callback';
+is $req->body, 'hello!', 'right content';
+is $req->content->on_read, undef, 'no read callback';
 $req->content(Mojo::Content::MultiPart->new);
 $req->body('hi!');
 is $req->body, 'hi!', 'right content';
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 42;
+use Test::More tests => 44;
 
 # Now that's a wave of destruction that's easy on the eyes.
 use_ok 'Mojo::Parameters';
@@ -59,6 +59,10 @@ is_deeply [$params->param], [qw/q t w/], 'right structure';
 $params->append('a', 4, 'a', 5, 'b', 6, 'b', 7);
 is_deeply $params->to_hash,
   {a => [4, 5], b => [6, 7], q => 1, w => 2, t => 7}, 'right structure';
+$params = Mojo::Parameters->new(foo => undef, bar => 'bar');
+is $params->to_string, 'foo=&bar=bar', 'right format';
+$params = Mojo::Parameters->new(bar => 'bar', foo => undef);
+is $params->to_string, 'bar=bar&foo=', 'right format';
 
 # 0 value
 $params = Mojo::Parameters->new(foo => 0);
@@ -105,7 +109,10 @@ is_deeply [$params->param('foo')], [qw/bar baz/], 'right values';
 is $params->param('a'), 'b', 'right value';
 is_deeply [$params->param('bar')], [qw/bas test/], 'right values';
 is_deeply $params->to_hash,
-  {foo => ['bar', 'baz'], a => 'b', bar => ['bas', 'test']},
+  { foo => ['bar', 'baz'],
+    a   => 'b',
+    bar => ['bas', 'test']
+  },
   'right structure';
 
 # Unicode
@@ -9,7 +9,7 @@ use Mojo::JSON;
 
 # We need some more secret sauce. Put the mayonnaise in the sun.
 use_ok 'Mojo::Server::PSGI';
-use_ok 'Mojo::Command::Psgi';
+use_ok 'Mojolicious::Command::Psgi';
 
 # Binding
 my $psgi    = Mojo::Server::PSGI->new;
@@ -70,7 +70,7 @@ $env = {
     'psgi.multiprocess' => 1,
     'psgi.run_once'     => 0
 };
-$app = Mojo::Command::Psgi->new->run;
+$app = Mojolicious::Command::Psgi->new->run;
 $res = $app->($env);
 is $res->[0], 200, 'right status';
 %headers = @{$res->[1]};
@@ -5,7 +5,7 @@ use warnings;
 
 use utf8;
 
-use Test::More tests => 154;
+use Test::More tests => 160;
 
 # I don't want you driving around in a car you built yourself.
 # You can sit there complaining, or you can knit me some seat belts.
@@ -270,11 +270,11 @@ is $url->is_ipv4, 1,           'is an IPv4 address';
 is $url->is_ipv6, undef,       'not an IPv6 address';
 $url = Mojo::URL->new('http://0::127.0.0.1');
 is $url->host,    '0::127.0.0.1', 'right host';
-is $url->is_ipv4, 1,              'is an IPv4 address';
+is $url->is_ipv4, undef,          'not an IPv4 address';
 is $url->is_ipv6, 1,              'is an IPv6 address';
 $url = Mojo::URL->new('http://[0::127.0.0.1]');
 is $url->host,    '[0::127.0.0.1]', 'right host';
-is $url->is_ipv4, 1,                'is an IPv4 address';
+is $url->is_ipv4, undef,            'not an IPv4 address';
 is $url->is_ipv6, 1,                'is an IPv6 address';
 $url = Mojo::URL->new('http://mojolicio.us:3000');
 is $url->host,    'mojolicio.us', 'right host';
@@ -290,9 +290,17 @@ is $url->is_ipv4, 1,           'is an IPv4 address';
 is $url->is_ipv6, undef,       'not an IPv6 address';
 $url = Mojo::URL->new('http://0::127.0.0.1:3000');
 is $url->host,    '0::127.0.0.1', 'right host';
-is $url->is_ipv4, 1,              'is an IPv4 address';
+is $url->is_ipv4, undef,          'not an IPv4 address';
 is $url->is_ipv6, 1,              'is an IPv6 address';
 $url = Mojo::URL->new('http://[0::127.0.0.1]:3000');
 is $url->host,    '[0::127.0.0.1]', 'right host';
-is $url->is_ipv4, 1,                'is an IPv4 address';
+is $url->is_ipv4, undef,            'not an IPv4 address';
 is $url->is_ipv6, 1,                'is an IPv6 address';
+$url = Mojo::URL->new('http://foo.1.1.1.1.de/');
+is $url->host,    'foo.1.1.1.1.de', 'right host';
+is $url->is_ipv4, undef,            'not an IPv4 address';
+is $url->is_ipv6, undef,            'not an IPv4 address';
+$url = Mojo::URL->new('http://1.1.1.1.1.1/');
+is $url->host,    '1.1.1.1.1.1', 'right host';
+is $url->is_ipv4, undef,         'not an IPv4 address';
+is $url->is_ipv6, undef,         'not an IPv4 address';
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 194;
 
@@ -5,8 +5,8 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 38;
 
@@ -0,0 +1,187 @@
+#!/usr/bin/env perl
+
+package Test::Foo;
+
+use strict;
+use warnings;
+
+use base 'Mojolicious::Controller';
+
+sub bar  {1}
+sub home {1}
+
+package Test::Controller;
+
+use strict;
+use warnings;
+
+use base 'Mojolicious::Controller';
+
+__PACKAGE__->attr('render_called');
+
+sub render { shift->render_called(1) }
+
+sub reset_state {
+    my $self = shift;
+    $self->render_called(0);
+    my $stash = $self->stash;
+    delete $stash->{$_} for keys %$stash;
+}
+
+# I was all of history's greatest acting robots -- Acting Unit 0.8,
+# Thespomat, David Duchovny!
+package main;
+
+use strict;
+use warnings;
+
+use utf8;
+
+use Test::More tests => 67;
+
+use Mojo;
+use Mojo::Transaction::HTTP;
+use Mojolicious::Controller;
+use Mojolicious::Routes;
+
+my $c = Mojolicious::Controller->new;
+
+# Set
+$c->stash(foo => 'bar');
+is $c->stash('foo'), 'bar', 'set and return a stash value';
+
+# Ref value
+my $stash = $c->stash;
+is_deeply $stash, {foo => 'bar'}, 'return a hashref';
+
+# Replace
+$c->stash(foo => 'baz');
+is $c->stash('foo'), 'baz', 'replace and return a stash value';
+
+# Set 0
+$c->stash(zero => 0);
+is $c->stash('zero'), 0, 'set and return 0 value';
+
+# Replace with 0
+$c->stash(foo => 0);
+is $c->stash('foo'), 0, 'replace and return 0 value';
+
+# Use 0 as key
+$c->stash(0 => 'boo');
+is $c->stash('0'), 'boo', 'set and get with 0 as key';
+
+# Delete
+$stash = $c->stash;
+delete $stash->{foo};
+delete $stash->{0};
+delete $stash->{zero};
+is_deeply $stash, {}, 'elements can be deleted';
+$c->stash('foo' => 'zoo');
+delete $c->stash->{foo};
+is_deeply $c->stash, {}, 'elements can be deleted';
+
+# Set via hash
+$c->stash({a => 1, b => 2});
+$stash = $c->stash;
+is_deeply $stash, {a => 1, b => 2}, 'set via hashref';
+
+$c = Test::Controller->new(app => Mojo->new);
+$c->app->log->path(undef);
+$c->app->log->level('fatal');
+my $d = Mojolicious::Routes->new;
+ok $d, 'initialized';
+
+$d->namespace('Test');
+$d->route('/')->to(controller => 'foo', action => 'home');
+$d->route('/foo/(capture)')->to(controller => 'foo', action => 'bar');
+
+# 404 clean stash
+$c->reset_state;
+my $tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/not_found');
+$c->tx($tx);
+is $d->dispatch($c), 1, 'dispatched';
+is_deeply $c->stash, {}, 'empty stash';
+ok !$c->render_called, 'nothing rendered';
+
+# No escaping
+$c->reset_state;
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('POST');
+$tx->req->url->parse('/foo/hello');
+$c->tx($tx);
+$c->stash(test => 23);
+is $d->dispatch($c), undef, 'dispatched';
+is $c->stash->{controller}, 'foo',   'right value';
+is $c->stash->{action},     'bar',   'right value';
+is $c->stash->{capture},    'hello', 'right value';
+is $c->stash->{test},       23,      'right value';
+is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
+is $c->param('controller'), 'foo',   'right value';
+is $c->param('action'),     'bar',   'right value';
+is $c->param('capture'),    'hello', 'right value';
+is_deeply [$c->param], [qw/action capture controller/], 'right names';
+$c->param(capture => 'bye');
+is $c->param('controller'), 'foo', 'right value';
+is $c->param('action'),     'bar', 'right value';
+is $c->param('capture'),    'bye', 'right value';
+is_deeply [$c->param], [qw/action capture controller/], 'right names';
+$c->param(capture => undef);
+is $c->param('controller'), 'foo', 'right value';
+is $c->param('action'),     'bar', 'right value';
+is $c->param('capture'),    undef, 'no value';
+is_deeply [$c->param], [qw/action capture controller/], 'right names';
+$c->req->param(foo => 'bar');
+is $c->param('controller'), 'foo', 'right value';
+is $c->param('action'),     'bar', 'right value';
+is $c->param('capture'),    undef, 'no value';
+is $c->param('foo'),        'bar', 'right value';
+is_deeply [$c->param], [qw/action capture controller foo/], 'right names';
+$c->req->param(bar => 'baz');
+is $c->param('controller'), 'foo', 'right value';
+is $c->param('action'),     'bar', 'right value';
+is $c->param('capture'),    undef, 'no value';
+is $c->param('foo'),        'bar', 'right value';
+is $c->param('bar'),        'baz', 'right value';
+is_deeply [$c->param], [qw/action bar capture controller foo/], 'right names';
+$c->req->param(action => 'baz');
+is $c->param('controller'), 'foo', 'right value';
+is $c->param('action'),     'bar', 'right value';
+is $c->param('capture'),    undef, 'no value';
+is $c->param('foo'),        'bar', 'right value';
+is $c->param('bar'),        'baz', 'right value';
+is_deeply [$c->param], [qw/action bar capture controller foo/], 'right names';
+ok $c->render_called, 'rendered';
+
+# Escaping
+$c->reset_state;
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/foo/hello%20there');
+$c->tx($tx);
+is $d->dispatch($c), undef, 'dispatched';
+is $c->stash->{controller}, 'foo',         'right value';
+is $c->stash->{action},     'bar',         'right value';
+is $c->stash->{capture},    'hello there', 'right value';
+is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
+is $c->param('controller'), 'foo',         'right value';
+is $c->param('action'),     'bar',         'right value';
+is $c->param('capture'),    'hello there', 'right value';
+ok $c->render_called, 'rendered';
+
+# Escaping utf8
+$c->reset_state;
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/foo/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82');
+$c->tx($tx);
+is $d->dispatch($c), undef, 'dispatched';
+is $c->stash->{controller}, 'foo',          'right value';
+is $c->stash->{action},     'bar',          'right value';
+is $c->stash->{capture},    'привет', 'right value';
+is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
+is $c->param('controller'), 'foo',          'right value';
+is $c->param('action'),     'bar',          'right value';
+is $c->param('capture'),    'привет', 'right value';
+ok $c->render_called, 'rendered';
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 9;
 
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 35;
 
@@ -5,9 +5,9 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
+# Disable epoll and kqueue
 BEGIN {
-    $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1;
+    $ENV{MOJO_POLL} = 1;
     $ENV{MOJO_MODE} = 'testing';
 }
 
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 24;
 
@@ -5,8 +5,8 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 8;
 
@@ -5,9 +5,9 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
+# Disable epoll and kqueue
 BEGIN {
-    $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1;
+    $ENV{MOJO_POLL} = 1;
     $ENV{MOJO_MODE} = 'testing';
 }
 
@@ -5,8 +5,8 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 664;
 
@@ -826,20 +826,28 @@ $t->get_ok('/tags/lala?a=b&b=0&c=2&d=3&escaped=1%22+%222')->status_is(200)
 <input name="escaped" value="1&quot; &quot;2" />
 <input name="a" value="b" />
 <input name="a" value="b" />
-<script src="script.js" type="text/javascript" />
-<script type="text/javascript"><![CDATA[
+<script src="script.js" type="text/javascript"></script>
+<script type="text/javascript">//<![CDATA[
+
     var a = 'b';
-]]></script>
-<script type="foo"><![CDATA[
+
+//]]></script>
+<script type="foo">//<![CDATA[
+
     var a = 'b';
-]]></script>
+
+//]]></script>
 <link href="foo.css" media="screen" rel="stylesheet" type="text/css" />
-<style type="text/css"><![CDATA[
+<style type="text/css">/*<![CDATA[*/
+
     body {color: #000}
-]]></style>
-<style type="foo"><![CDATA[
+
+/*]]>*/</style>
+<style type="foo">/*<![CDATA[*/
+
     body {color: #000}
-]]></style>
+
+/*]]>*/</style>
 EOF
 
 # GET /tags (alternative)
@@ -876,20 +884,28 @@ $t->get_ok('/tags/lala?c=b&d=3&e=4&f=5')->status_is(200)->content_is(<<EOF);
 <input name="escaped" />
 <input name="a" />
 <input name="a" value="c" />
-<script src="script.js" type="text/javascript" />
-<script type="text/javascript"><![CDATA[
+<script src="script.js" type="text/javascript"></script>
+<script type="text/javascript">//<![CDATA[
+
     var a = 'b';
-]]></script>
-<script type="foo"><![CDATA[
+
+//]]></script>
+<script type="foo">//<![CDATA[
+
     var a = 'b';
-]]></script>
+
+//]]></script>
 <link href="foo.css" media="screen" rel="stylesheet" type="text/css" />
-<style type="text/css"><![CDATA[
+<style type="text/css">/*<![CDATA[*/
+
     body {color: #000}
-]]></style>
-<style type="foo"><![CDATA[
+
+/*]]>*/</style>
+<style type="foo">/*<![CDATA[*/
+
     body {color: #000}
-]]></style>
+
+/*]]>*/</style>
 EOF
 
 # GET /selection (empty)
@@ -996,7 +1012,7 @@ $t->get_ok('/foo_wildcard/')->status_is(404);
 
 # GET /with/header/condition
 $t->get_ok('/with/header/condition', {'X-Secret-Header' => 'bar'})
-  ->status_is(200)->content_like(qr/^Test ok/);
+  ->status_is(200)->content_like(qr/^Test ok<base href="http:\/\/localhost/);
 
 # GET /with/header/condition (not found)
 $t->get_ok('/with/header/condition')->status_is(404)->content_like(qr/Oops!/);
@@ -1658,8 +1674,8 @@ text!
 @@ static2.txt (base64)
 dGVzdCAxMjMKbGFsYWxh
 
-@@ with_header_condition.html.epl
-Test ok
+@@ with_header_condition.html.ep
+Test ok<%= base_tag %>
 
 @@ template_inheritance.html.ep
 % layout 'template_inheritance';
@@ -3,10 +3,10 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
-use Test::More tests => 49;
+use Test::More tests => 69;
 
 # I was God once.
 # Yes, I saw. You were doing well until everyone died.
@@ -14,10 +14,11 @@ use Mojolicious::Lite;
 use Test::Mojo;
 
 # GET /shortpoll
-my $shortpoll;
+my $shortpoll = 0;
 get '/shortpoll' => sub {
     my $self = shift;
-    $self->on_finish(sub { $shortpoll = 'finished!' });
+    $self->res->headers->connection('close');
+    $self->on_finish(sub { $shortpoll++ });
     $self->res->code(200);
     $self->res->headers->content_type('text/plain');
     $self->write_chunk('this was short.');
@@ -70,13 +71,13 @@ get '/longpoll/nested' => sub {
 my $longpoll_plain;
 get '/longpoll/plain' => sub {
     my $self = shift;
-    $self->on_finish(sub { $longpoll_plain = 'finished!' });
     $self->res->code(200);
     $self->res->headers->content_type('text/plain');
     $self->res->headers->content_length(25);
     $self->write('hi ');
     $self->client->ioloop->timer(
         '0.5' => sub {
+            $self->on_finish(sub { $longpoll_plain = 'finished!' });
             $self->write('there plain,', sub { shift->write(' whats up?') });
         }
     );
@@ -126,6 +127,36 @@ get '/longpoll/plain/delayed' => sub {
     );
 };
 
+# GET /longpoll/static/delayed
+my $longpoll_static_delayed;
+get '/longpoll/static/delayed' => sub {
+    my $self = shift;
+    $self->on_finish(sub { $longpoll_static_delayed = 'finished!' });
+    $self->client->ioloop->timer(
+        '0.5' => sub { $self->render_static('hello.txt') });
+};
+
+# GET /too_long
+my $too_long;
+get '/too_long' => sub {
+    my $self = shift;
+    $self->on_finish(sub { $too_long = 'finished!' });
+    $self->res->code(200);
+    $self->res->headers->content_type('text/plain');
+    $self->res->headers->content_length(12);
+    $self->write('how');
+    $self->client->ioloop->timer(
+        '5' => sub {
+            $self->write(
+                sub {
+                    my $self = shift;
+                    $self->write('dy plain!');
+                }
+            );
+        }
+    );
+};
+
 my $t = Test::Mojo->new;
 
 # GET /shortpoll
@@ -133,13 +164,17 @@ $t->get_ok('/shortpoll')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_type_is('text/plain')->content_is('this was short.');
-is $shortpoll, 'finished!', 'finished';
+is $t->tx->kept_alive, undef, 'connection was not kept alive';
+is $t->tx->keep_alive, 0,     'connection will not be kept alive';
+is $shortpoll, 1, 'finished';
 
 # GET /shortpoll/plain
 $t->get_ok('/shortpoll/plain')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_type_is('text/plain')->content_is('this was short and plain.');
+is $t->tx->kept_alive, undef, 'connection was not kept alive';
+is $t->tx->keep_alive, 1,     'connection will be kept alive';
 is $shortpoll_plain, 'finished!', 'finished';
 
 # GET /longpoll
@@ -147,8 +182,44 @@ $t->get_ok('/longpoll')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_type_is('text/plain')->content_is('hi there, whats up?');
+is $t->tx->kept_alive, 1, 'connection was kept alive';
+is $t->tx->keep_alive, 1, 'connection will be kept alive';
 is $longpoll, 'finished!', 'finished';
 
+# GET /longpoll (interrupted)
+$longpoll = undef;
+my $port = $t->client->test_server;
+$t->client->ioloop->connect(
+    address    => 'localhost',
+    port       => $port,
+    on_connect => sub {
+        my ($self, $id) = @_;
+        $self->write($id => "GET /longpoll HTTP/1.1\x0d\x0a\x0d\x0a");
+    },
+    on_read => sub {
+        my ($self, $id, $chunk) = @_;
+        $self->drop($id);
+        $self->timer('0.5', sub { shift->stop });
+    }
+);
+$t->client->ioloop->start;
+is $longpoll, 'finished!', 'finished';
+
+# GET /longpoll (also interrupted)
+my $tx = $t->client->build_tx(GET => '/longpoll');
+my $buffer = '';
+$tx->res->body(
+    sub {
+        my ($self, $chunk) = @_;
+        $buffer .= $chunk;
+        $self->error('Interrupted!');
+    }
+);
+$t->client->process($tx);
+is $tx->res->code,  200,            'right status';
+is $tx->res->error, 'Interrupted!', 'right error';
+is $buffer, 'hi ', 'right content';
+
 # GET /longpoll/nested
 $t->get_ok('/longpoll/nested')->status_is(200)
   ->header_is(Server         => 'Mojolicious (Perl)')
@@ -176,3 +247,25 @@ $t->get_ok('/longpoll/plain/delayed')->status_is(200)
   ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
   ->content_type_is('text/plain')->content_is('howdy plain!');
 is $longpoll_plain_delayed, 'finished!', 'finished';
+
+# GET /longpoll/static/delayed
+$t->get_ok('/longpoll/static/delayed')->status_is(200)
+  ->header_is(Server         => 'Mojolicious (Perl)')
+  ->header_is('X-Powered-By' => 'Mojolicious (Perl)')
+  ->content_type_is('text/plain')
+  ->content_is('Hello Mojo from a static file!');
+is $longpoll_static_delayed, 'finished!', 'finished';
+
+# GET /too_long (timeout)
+$tx = $t->client->keep_alive_timeout(1)->build_tx(GET => '/too_long');
+$buffer = '';
+$tx->res->body(
+    sub {
+        my ($self, $chunk) = @_;
+        $buffer .= $chunk;
+    }
+);
+$t->client->process($tx);
+is $tx->res->code, 200, 'right status';
+is $tx->error, 'Interrupted, maybe a timeout?', 'right error';
+is $buffer, 'how', 'right content';
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 38;
+
+# People said I was dumb, but I proved them.
+use_ok 'Mojolicious::Routes::Pattern';
+
+# Normal pattern with text, symbols and a default value
+my $pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action');
+$pattern->defaults({action => 'index'});
+my $result = $pattern->match('/test/foo/bar');
+is $result->{controller}, 'foo', 'right value';
+is $result->{action},     'bar', 'right value';
+$result = $pattern->match('/test/foo');
+is $result->{controller}, 'foo',   'right value';
+is $result->{action},     'index', 'right value';
+$result = $pattern->match('/test/foo/');
+is $result->{controller}, 'foo',   'right value';
+is $result->{action},     'index', 'right value';
+$result = $pattern->match('/test/');
+is $result, undef, 'no result';
+is $pattern->render({controller => 'foo'}), '/test/foo', 'right result';
+
+# Root
+$pattern = Mojolicious::Routes::Pattern->new('/');
+$pattern->defaults({action => 'index'});
+$result = $pattern->match('/test/foo/bar');
+is $result, undef, 'no result';
+$result = $pattern->match('/');
+is $result->{action}, 'index', 'right value';
+is $pattern->render, '/', 'right result';
+
+# Regex in pattern
+$pattern =
+  Mojolicious::Routes::Pattern->new('/test/(controller)/:action/(id)',
+    id => '\d+');
+$pattern->defaults({action => 'index', id => 1});
+$result = $pattern->match('/test/foo/bar/203');
+is $result->{controller}, 'foo', 'right value';
+is $result->{action},     'bar', 'right value';
+is $result->{id},         203,   'right value';
+$result = $pattern->match('/test/foo/bar/baz');
+is_deeply $result, undef, 'no result';
+is $pattern->render({controller => 'zzz', action => 'index', id => 13}),
+  '/test/zzz/index/13', 'right result';
+is $pattern->render({controller => 'zzz'}), '/test/zzz', 'right result';
+
+# Quoted symbol
+$pattern = Mojolicious::Routes::Pattern->new('/(:controller)test/(action)');
+$pattern->defaults({action => 'index'});
+$result = $pattern->match('/footest/bar');
+is $result->{controller}, 'foo', 'right value';
+is $result->{action},     'bar', 'right value';
+is $pattern->render({controller => 'zzz', action => 'lala'}), '/zzztest/lala',
+  'right result';
+$result = $pattern->match('/test/lala');
+is $result, undef, 'no result';
+
+# Format
+$pattern = Mojolicious::Routes::Pattern->new('/(controller)test/(action)');
+is $pattern->format, undef, 'no value';
+$pattern =
+  Mojolicious::Routes::Pattern->new('/(:controller)test/:action.html');
+is $pattern->format, 'html', 'right value';
+$pattern = Mojolicious::Routes::Pattern->new('/index.cgi');
+is $pattern->format, 'cgi', 'right value';
+
+# Relaxed
+$pattern = Mojolicious::Routes::Pattern->new('/test/(.controller)/:action');
+$result  = $pattern->match('/test/foo.bar/baz');
+is $result->{controller}, 'foo.bar', 'right value';
+is $result->{action},     'baz',     'right value';
+is $pattern->render({controller => 'foo.bar', action => 'baz'}),
+  '/test/foo.bar/baz', 'right result';
+$pattern = Mojolicious::Routes::Pattern->new('/test/(.groovy)');
+$result  = $pattern->match('/test/foo.bar');
+is $pattern->format, undef, 'no value';
+is $result->{groovy}, 'foo.bar', 'right value';
+is $result->{format}, undef,     'no value';
+is $pattern->render({groovy => 'foo.bar'}), '/test/foo.bar', 'right result';
+
+# Wildcard
+$pattern = Mojolicious::Routes::Pattern->new('/test/(:controller)/(*action)');
+$result  = $pattern->match('/test/foo/bar.baz/yada');
+is $result->{controller}, 'foo',          'right value';
+is $result->{action},     'bar.baz/yada', 'right value';
+is $pattern->render({controller => 'foo', action => 'bar.baz/yada'}),
+  '/test/foo/bar.baz/yada', 'right result';
+
+# Render false value
+$pattern = Mojolicious::Routes::Pattern->new('/:id');
+is $pattern->render({id => 0}), '/0', 'right result';
+
+# Regex in path
+$pattern = Mojolicious::Routes::Pattern->new('/:test');
+$result  = $pattern->match('/test(test)(\Qtest\E)(');
+is $result->{test}, 'test(test)(\Qtest\E)(', 'right value';
+is $pattern->render({test => '23'}), '/23', 'right result';
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 plan skip_all => 'Perl 5.10 required for this test!'
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 26;
 
@@ -0,0 +1,47 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 5;
+
+use Mojolicious;
+use Mojolicious::Controller;
+use Mojolicious::Renderer;
+
+# Actually, she wasn't really my girlfriend,
+# she just lived nextdoor and never closed her curtains.
+my $c = Mojolicious::Controller->new(app => Mojolicious->new);
+$c->app->log->path(undef);
+$c->app->log->level('fatal');
+my $r = Mojolicious::Renderer->new(default_format => 'debug');
+$r->add_handler(
+    debug => sub {
+        my ($self, $c, $output) = @_;
+        $$output .= 'Hello Mojo!';
+    }
+);
+$c->stash->{format} = 'something';
+
+# Normal rendering
+$c->stash->{template} = 'something';
+$c->stash->{handler}  = 'debug';
+is_deeply [$r->render($c)], ['Hello Mojo!', 'text/plain'], 'normal rendering';
+
+# Normal rendering with layout
+$c->stash->{template} = 'something';
+$c->stash->{layout}   = 'something';
+$c->stash->{handler}  = 'debug';
+is_deeply [$r->render($c)], ['Hello Mojo!Hello Mojo!', 'text/plain'],
+  'normal rendering with layout';
+is delete $c->stash->{layout}, 'something';
+
+# Rendering a path with dots
+$c->stash->{template} = 'some.path.with.dots/template';
+$c->stash->{handler}  = 'debug';
+is_deeply [$r->render($c)], ['Hello Mojo!', 'text/plain'],
+  'rendering a path with dots';
+
+# Unrecognized handler
+$c->stash->{handler} = 'not_defined';
+is $r->render($c), undef, 'return undef for unrecognized handler';
@@ -0,0 +1,515 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 205;
+
+use Mojo::Transaction::HTTP;
+
+# They're not very heavy, but you don't hear me not complaining.
+use_ok 'Mojolicious::Routes';
+use_ok 'Mojolicious::Routes::Match';
+
+# Routes
+my $r = Mojolicious::Routes->new;
+
+# /clean
+$r->route('/clean')->to(clean => 1);
+
+# /clean/too
+$r->route('/clean/too')->to(something => 1);
+
+# /*/test
+my $test = $r->route('/:controller/test')->to(action => 'test');
+
+# /*/test/edit
+$test->route('/edit')->to(action => 'edit')->name('test_edit');
+
+# /*/test/delete/*
+$test->route('/delete/(id)', id => qr/\d+/)->to(action => 'delete', id => 23);
+
+# /test2
+my $test2 = $r->bridge('/test2')->to(controller => 'test2');
+
+# /test2 (inline)
+my $test4 = $test2->bridge->to(controller => 'index');
+
+# /test2/foo
+$test4->route('/foo')->to(controller => 'baz');
+
+# /test2/bar
+$test4->route('/bar')->to(controller => 'lalala');
+
+# /test2/baz
+$test2->route('/baz')->to('just#works');
+
+# /test3
+my $test3 = $r->waypoint('/test3')->to(controller => 's', action => 'l');
+
+# /test3/edit
+$test3->route('/edit')->to(action => 'edit');
+
+# /
+$r->route('/')->to(controller => 'hello', action => 'world');
+
+# /wildcards/1/*
+$r->route('/wildcards/1/(*wildcard)', wildcard => qr/(.*)/)
+  ->to(controller => 'wild', action => 'card');
+
+# /wildcards/2/*
+$r->route('/wildcards/2/(*wildcard)')
+  ->to(controller => 'card', action => 'wild');
+
+# /wildcards/3/*/foo
+$r->route('/wildcards/3/(*wildcard)/foo')
+  ->to(controller => 'very', action => 'dangerous');
+
+# /format
+# /format.html
+$r->route('/format')
+  ->to(controller => 'hello', action => 'you', format => 'html');
+
+# /format2.html
+$r->route('/format2.html')->to(controller => 'you', action => 'hello');
+
+# /format2.json
+$r->route('/format2.json')->to(controller => 'you', action => 'hello_json');
+
+# /format3/*.html
+$r->route('/format3/:foo.html')->to(controller => 'me', action => 'bye');
+
+# /format3/*.json
+$r->route('/format3/:foo.json')->to(controller => 'me', action => 'bye_json');
+
+# /articles
+# /articles.html
+# /articles/1
+# /articles/1.html
+# /articles/1/edit
+# /articles/1/delete
+my $articles = $r->waypoint('/articles')->to(
+    controller => 'articles',
+    action     => 'index',
+    format     => 'html'
+);
+my $wp = $articles->waypoint('/:id')->to(
+    controller => 'articles',
+    action     => 'load',
+    format     => 'html'
+);
+my $bridge = $wp->bridge->to(
+    controller => 'articles',
+    action     => 'load',
+    format     => 'html'
+);
+$bridge->route('/edit')->to(controller => 'articles', action => 'edit');
+$bridge->route('/delete')->to(
+    controller => 'articles',
+    action     => 'delete',
+    format     => undef
+)->name('articles_delete');
+
+# GET /method/get
+$r->route('/method/get')->via('GET')
+  ->to(controller => 'method', action => 'get');
+
+# POST /method/post
+$r->route('/method/post')->via('post')
+  ->to(controller => 'method', action => 'post');
+
+# POST|GET /method/post_get
+$r->route('/method/post_get')->via(qw/POST get/)
+  ->to(controller => 'method', action => 'post_get');
+
+# /simple/form
+$r->route('/simple/form')->to('test-test#test');
+
+# /edge/gift
+my $edge = $r->route('/edge');
+my $auth = $edge->bridge('/auth')->to('auth#check');
+$auth->route('/about/')->to('pref#about');
+$auth->bridge->to('album#allow')->route('/album/create/')->to('album#create');
+$auth->route('/gift/')->to('gift#index')->name('gift');
+
+# Make sure stash stays clean
+my $tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/clean');
+my $m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{clean},     1,     'right value';
+is $m->stack->[0]->{something}, undef, 'no value';
+is $m->url_for, '/clean', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/clean/too');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{clean},     undef, 'no value';
+is $m->stack->[0]->{something}, 1,     'right value';
+is $m->url_for, '/clean/too', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Real world example using most features at once
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/articles.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'articles', 'right value';
+is $m->stack->[0]->{action},     'index',    'right value';
+is $m->stack->[0]->{format},     'html',     'right value';
+is $m->url_for, '/articles', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/articles/1.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'articles', 'right value';
+is $m->stack->[0]->{action},     'load',     'right value';
+is $m->stack->[0]->{id},         '1',        'right value';
+is $m->stack->[0]->{format},     'html',     'right value';
+is $m->url_for(format => 'html'), '/articles/1.html', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/articles/1/edit');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[1]->{controller}, 'articles', 'right value';
+is $m->stack->[1]->{action},     'edit',     'right value';
+is $m->stack->[1]->{format},     'html',     'right value';
+is $m->url_for, '/articles/1/edit', 'right URL';
+is $m->url_for(format => 'html'), '/articles/1/edit.html', 'right URL';
+is $m->url_for('articles_delete', format => undef), '/articles/delete',
+  'right URL';
+is $m->url_for('articles_delete'), '/articles/delete', 'right URL';
+is $m->url_for('articles_delete', id => 12), '/articles/12/delete',
+  'right URL';
+is @{$m->stack}, 2, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/articles/1/delete');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[1]->{controller}, 'articles', 'right value';
+is $m->stack->[1]->{action},     'delete',   'right value';
+is $m->stack->[1]->{format},     undef,      'no value';
+is $m->url_for, '/articles/1/delete', 'right URL';
+is @{$m->stack}, 2, 'right number of elements';
+
+# Root
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->captures->{controller}, 'hello', 'right value';
+is $m->captures->{action},     'world', 'right value';
+is $m->stack->[0]->{controller}, 'hello', 'right value';
+is $m->stack->[0]->{action},     'world', 'right value';
+is $m->url_for, '/', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Path and captures
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/foo/test/edit');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->captures->{controller}, 'foo',  'right value';
+is $m->captures->{action},     'edit', 'right value';
+is $m->stack->[0]->{controller}, 'foo',  'right value';
+is $m->stack->[0]->{action},     'edit', 'right value';
+is $m->url_for, '/foo/test/edit', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Optional captures in sub route with requirement
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/bar/test/delete/22');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->captures->{controller}, 'bar',    'right value';
+is $m->captures->{action},     'delete', 'right value';
+is $m->captures->{id},         22,       'right value';
+is $m->stack->[0]->{controller}, 'bar',    'right value';
+is $m->stack->[0]->{action},     'delete', 'right value';
+is $m->stack->[0]->{id},         22,       'right value';
+is $m->url_for, '/bar/test/delete/22', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Defaults in sub route
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/bar/test/delete');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->captures->{controller}, 'bar',    'right value';
+is $m->captures->{action},     'delete', 'right value';
+is $m->captures->{id},         23,       'right value';
+is $m->stack->[0]->{controller}, 'bar',    'right value';
+is $m->stack->[0]->{action},     'delete', 'right value';
+is $m->stack->[0]->{id},         23,       'right value';
+is $m->url_for, '/bar/test/delete', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Chained routes
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test2/foo');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'test2', 'right value';
+is $m->stack->[1]->{controller}, 'index', 'right value';
+is $m->stack->[2]->{controller}, 'baz',   'right value';
+is $m->captures->{controller}, 'baz', 'right value';
+is $m->url_for, '/test2/foo', 'right URL';
+is @{$m->stack}, 3, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test2/bar');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'test2',  'right value';
+is $m->stack->[1]->{controller}, 'index',  'right value';
+is $m->stack->[2]->{controller}, 'lalala', 'right value';
+is $m->captures->{controller}, 'lalala', 'right value';
+is $m->url_for, '/test2/bar', 'right URL';
+is @{$m->stack}, 3, 'right number of elements';
+$tx->req->url->parse('/test2/baz');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'test2', 'right value';
+is $m->stack->[1]->{controller}, 'just',  'right value';
+is $m->stack->[1]->{action},     'works', 'right value';
+is $m->stack->[2], undef, 'no value';
+is $m->captures->{controller}, 'just', 'right value';
+is $m->url_for, '/test2/baz', 'right URL';
+is @{$m->stack}, 2, 'right number of elements';
+
+# Waypoints
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test3');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 's', 'right value';
+is $m->stack->[0]->{action},     'l', 'right value';
+is $m->url_for, '/test3', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test3/');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 's', 'right value';
+is $m->stack->[0]->{action},     'l', 'right value';
+is $m->url_for, '/test3', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test3/edit');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 's',    'right value';
+is $m->stack->[0]->{action},     'edit', 'right value';
+is $m->url_for, '/test3/edit', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Named url_for
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/test3');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->url_for, '/test3', 'right URL';
+is $m->url_for('test_edit', controller => 'foo'), '/foo/test/edit',
+  'right URL';
+is $m->url_for('test_edit', {controller => 'foo'}), '/foo/test/edit',
+  'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Wildcards
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/wildcards/1/hello/there');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'wild',        'right value';
+is $m->stack->[0]->{action},     'card',        'right value';
+is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
+is $m->url_for, '/wildcards/1/hello/there', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/wildcards/2/hello/there');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'card',        'right value';
+is $m->stack->[0]->{action},     'wild',        'right value';
+is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
+is $m->url_for, '/wildcards/2/hello/there', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/wildcards/3/hello/there/foo');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'very',        'right value';
+is $m->stack->[0]->{action},     'dangerous',   'right value';
+is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
+is $m->url_for, '/wildcards/3/hello/there/foo', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Escaped
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/wildcards/1/http://www.google.com');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'wild',                  'right value';
+is $m->stack->[0]->{action},     'card',                  'right value';
+is $m->stack->[0]->{wildcard},   'http://www.google.com', 'right value';
+is $m->url_for, '/wildcards/1/http://www.google.com', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/wildcards/1/http%3A%2F%2Fwww.google.com');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'wild',                  'right value';
+is $m->stack->[0]->{action},     'card',                  'right value';
+is $m->stack->[0]->{wildcard},   'http://www.google.com', 'right value';
+is $m->url_for, '/wildcards/1/http://www.google.com', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Format
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'hello', 'right value';
+is $m->stack->[0]->{action},     'you',   'right value';
+is $m->stack->[0]->{format},     'html',  'right value';
+is $m->url_for, '/format', 'right URL';
+is $m->url_for(format => undef),  '/format',      'right URL';
+is $m->url_for(format => 'html'), '/format.html', 'right URL';
+is $m->url_for(format => 'txt'),  '/format.txt',  'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'hello', 'right value';
+is $m->stack->[0]->{action},     'you',   'right value';
+is $m->stack->[0]->{format},     'html',  'right value';
+is $m->url_for, '/format', 'right URL';
+is $m->url_for(format => undef),  '/format',      'right URL';
+is $m->url_for(format => 'html'), '/format.html', 'right URL';
+is $m->url_for(format => 'txt'),  '/format.txt',  'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format2.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'you',   'right value';
+is $m->stack->[0]->{action},     'hello', 'right value';
+is $m->stack->[0]->{format},     'html',  'right value';
+is $m->url_for, '/format2.html', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format2.json');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'you',        'right value';
+is $m->stack->[0]->{action},     'hello_json', 'right value';
+is $m->stack->[0]->{format},     'json',       'right value';
+is $m->url_for, '/format2.json', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format3/baz.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'me',   'right value';
+is $m->stack->[0]->{action},     'bye',  'right value';
+is $m->stack->[0]->{format},     'html', 'right value';
+is $m->stack->[0]->{foo},        'baz',  'right value';
+is $m->url_for, '/format3/baz.html', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/format3/baz.json');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'me',       'right value';
+is $m->stack->[0]->{action},     'bye_json', 'right value';
+is $m->stack->[0]->{format},     'json',     'right value';
+is $m->stack->[0]->{foo},        'baz',      'right value';
+is $m->url_for, '/format3/baz.json', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Request methods
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/method/get.html');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'method', 'right value';
+is $m->stack->[0]->{action},     'get',    'right value';
+is $m->stack->[0]->{format},     'html',   'right value';
+is $m->url_for, '/method/get', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('POST');
+$tx->req->url->parse('/method/post');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'method', 'right value';
+is $m->stack->[0]->{action},     'post',   'right value';
+is $m->stack->[0]->{format},     undef,    'no value';
+is $m->url_for, '/method/post', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/method/post_get');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'method',   'right value';
+is $m->stack->[0]->{action},     'post_get', 'right value';
+is $m->stack->[0]->{format},     undef,      'no value';
+is $m->url_for, '/method/post_get', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('POST');
+$tx->req->url->parse('/method/post_get');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'method',   'right value';
+is $m->stack->[0]->{action},     'post_get', 'right value';
+is $m->stack->[0]->{format},     undef,      'no value';
+is $m->url_for, '/method/post_get', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('DELETE');
+$tx->req->url->parse('/method/post_get');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, undef, 'no value';
+is $m->stack->[0]->{action},     undef, 'no value';
+is $m->stack->[0]->{format},     undef, 'no value';
+is $m->url_for, '', 'no URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Not found
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/not_found');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->url_for('test_edit', controller => 'foo'), '/foo/test/edit',
+  'right URL';
+is @{$m->stack}, 0, 'no elements';
+
+# Simplified form
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/simple/form');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'test-test', 'right value';
+is $m->stack->[0]->{action},     'test',      'right value';
+is $m->stack->[0]->{format},     undef,       'no value';
+is $m->url_for, '/simple/form', 'right URL';
+is $m->url_for('current'), '/simple/form', 'right URL';
+is @{$m->stack}, 1, 'right number of elements';
+
+# Special edge case with nested bridges
+$tx = Mojo::Transaction::HTTP->new;
+$tx->req->method('GET');
+$tx->req->url->parse('/edge/auth/gift');
+$m = Mojolicious::Routes::Match->new($tx)->match($r);
+is $m->stack->[0]->{controller}, 'auth',  'right value';
+is $m->stack->[0]->{action},     'check', 'right value';
+is $m->stack->[0]->{format},     undef,   'no value';
+is $m->stack->[1]->{controller}, 'gift',  'right value';
+is $m->stack->[1]->{action},     'index', 'right value';
+is $m->stack->[1]->{format},     undef,   'no value';
+is $m->stack->[2], undef, 'no value';
+is $m->url_for, '/edge/auth/gift', 'right URL';
+is $m->url_for('gift'),    '/edge/auth/gift', 'right URL';
+is $m->url_for('current'), '/edge/auth/gift', 'right URL';
+is @{$m->stack}, 2, 'right number of elements';
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 plan skip_all => 'Perl 5.10 required for this test!'
@@ -5,8 +5,8 @@ use warnings;
 
 use utf8;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 plan skip_all => 'Windows is too fragile for this test!' if $^O eq 'MSWin32';
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 29;
 
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More tests => 9;
 
@@ -3,12 +3,12 @@
 use strict;
 use warnings;
 
-# Disable epoll, kqueue and IPv6
-BEGIN { $ENV{MOJO_POLL} = $ENV{MOJO_NO_IPV6} = 1 }
+# Disable epoll and kqueue
+BEGIN { $ENV{MOJO_POLL} = 1 }
 
 use Test::More;
 use Mojo::IOLoop;
-plan skip_all => 'IO::Socket::SSL 1.33 required for this test!'
+plan skip_all => 'IO::Socket::SSL 1.34 required for this test!'
   unless Mojo::IOLoop::TLS;
 plan tests => 16;
 
@@ -1,156 +0,0 @@
-#!/usr/bin/env perl
-
-package Test::Foo;
-
-use strict;
-use warnings;
-
-use base 'MojoX::Dispatcher::Routes::Controller';
-
-sub bar  {1}
-sub home {1}
-
-package Test::Controller;
-
-use strict;
-use warnings;
-
-use base 'MojoX::Dispatcher::Routes::Controller';
-
-__PACKAGE__->attr('render_called');
-
-sub render { shift->render_called(1) }
-
-sub reset_state {
-    my $self = shift;
-    $self->render_called(0);
-    my $stash = $self->stash;
-    delete $stash->{$_} for keys %$stash;
-}
-
-# I was all of history's greatest acting robots -- Acting Unit 0.8,
-# Thespomat, David Duchovny!
-package main;
-
-use strict;
-use warnings;
-
-use utf8;
-
-use Test::More tests => 41;
-
-use Mojo;
-use Mojo::Transaction::HTTP;
-use MojoX::Dispatcher::Routes;
-use MojoX::Dispatcher::Routes::Controller;
-
-my $c = MojoX::Dispatcher::Routes::Controller->new;
-
-# Set
-$c->stash(foo => 'bar');
-is $c->stash('foo'), 'bar', 'set and return a stash value';
-
-# Ref value
-my $stash = $c->stash;
-is_deeply $stash, {foo => 'bar'}, 'return a hashref';
-
-# Replace
-$c->stash(foo => 'baz');
-is $c->stash('foo'), 'baz', 'replace and return a stash value';
-
-# Set 0
-$c->stash(zero => 0);
-is $c->stash('zero'), 0, 'set and return 0 value';
-
-# Replace with 0
-$c->stash(foo => 0);
-is $c->stash('foo'), 0, 'replace and return 0 value';
-
-# Use 0 as key
-$c->stash(0 => 'boo');
-is $c->stash('0'), 'boo', 'set and get with 0 as key';
-
-# Delete
-$stash = $c->stash;
-delete $stash->{foo};
-delete $stash->{0};
-delete $stash->{zero};
-is_deeply $stash, {}, 'elements can be deleted';
-$c->stash('foo' => 'zoo');
-delete $c->stash->{foo};
-is_deeply $c->stash, {}, 'elements can be deleted';
-
-# Set via hash
-$c->stash({a => 1, b => 2});
-$stash = $c->stash;
-is_deeply $stash, {a => 1, b => 2}, 'set via hashref';
-
-$c = Test::Controller->new(app => Mojo->new);
-$c->app->log->path(undef);
-$c->app->log->level('fatal');
-my $d = MojoX::Dispatcher::Routes->new;
-ok $d, 'initialized';
-
-$d->namespace('Test');
-$d->route('/')->to(controller => 'foo', action => 'home');
-$d->route('/foo/(capture)')->to(controller => 'foo', action => 'bar');
-
-# 404 clean stash
-$c->reset_state;
-my $tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/not_found');
-$c->tx($tx);
-is $d->dispatch($c), 1, 'dispatched';
-is_deeply $c->stash, {}, 'empty stash';
-ok !$c->render_called, 'nothing rendered';
-
-# No escaping
-$c->reset_state;
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('POST');
-$tx->req->url->parse('/foo/hello');
-$c->tx($tx);
-$c->stash(test => 23);
-is $d->dispatch($c), undef, 'dispatched';
-is $c->stash->{controller}, 'foo',   'right value';
-is $c->stash->{action},     'bar',   'right value';
-is $c->stash->{capture},    'hello', 'right value';
-is $c->stash->{test},       23,      'right value';
-is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
-is $c->param('controller'), 'foo',   'right value';
-is $c->param('action'),     'bar',   'right value';
-is $c->param('capture'),    'hello', 'right value';
-ok $c->render_called, 'rendered';
-
-# Escaping
-$c->reset_state;
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/foo/hello%20there');
-$c->tx($tx);
-is $d->dispatch($c), undef, 'dispatched';
-is $c->stash->{controller}, 'foo',         'right value';
-is $c->stash->{action},     'bar',         'right value';
-is $c->stash->{capture},    'hello there', 'right value';
-is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
-is $c->param('controller'), 'foo',         'right value';
-is $c->param('action'),     'bar',         'right value';
-is $c->param('capture'),    'hello there', 'right value';
-ok $c->render_called, 'rendered';
-
-# Escaping utf8
-$c->reset_state;
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/foo/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82');
-$c->tx($tx);
-is $d->dispatch($c), undef, 'dispatched';
-is $c->stash->{controller}, 'foo',          'right value';
-is $c->stash->{action},     'bar',          'right value';
-is $c->stash->{capture},    'привет', 'right value';
-is ref $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
-is $c->param('controller'), 'foo',          'right value';
-is $c->param('action'),     'bar',          'right value';
-is $c->param('capture'),    'привет', 'right value';
-ok $c->render_called, 'rendered';
@@ -1,99 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use Test::More tests => 38;
-
-# People said I was dumb, but I proved them.
-use_ok 'MojoX::Routes::Pattern';
-
-# Normal pattern with text, symbols and a default value
-my $pattern = MojoX::Routes::Pattern->new('/test/(controller)/:action');
-$pattern->defaults({action => 'index'});
-my $result = $pattern->match('/test/foo/bar');
-is $result->{controller}, 'foo', 'right value';
-is $result->{action},     'bar', 'right value';
-$result = $pattern->match('/test/foo');
-is $result->{controller}, 'foo',   'right value';
-is $result->{action},     'index', 'right value';
-$result = $pattern->match('/test/foo/');
-is $result->{controller}, 'foo',   'right value';
-is $result->{action},     'index', 'right value';
-$result = $pattern->match('/test/');
-is $result, undef, 'no result';
-is $pattern->render({controller => 'foo'}), '/test/foo', 'right result';
-
-# Root
-$pattern = MojoX::Routes::Pattern->new('/');
-$pattern->defaults({action => 'index'});
-$result = $pattern->match('/test/foo/bar');
-is $result, undef, 'no result';
-$result = $pattern->match('/');
-is $result->{action}, 'index', 'right value';
-is $pattern->render, '/', 'right result';
-
-# Regex in pattern
-$pattern =
-  MojoX::Routes::Pattern->new('/test/(controller)/:action/(id)', id => '\d+');
-$pattern->defaults({action => 'index', id => 1});
-$result = $pattern->match('/test/foo/bar/203');
-is $result->{controller}, 'foo', 'right value';
-is $result->{action},     'bar', 'right value';
-is $result->{id},         203,   'right value';
-$result = $pattern->match('/test/foo/bar/baz');
-is_deeply $result, undef, 'no result';
-is $pattern->render({controller => 'zzz', action => 'index', id => 13}),
-  '/test/zzz/index/13', 'right result';
-is $pattern->render({controller => 'zzz'}), '/test/zzz', 'right result';
-
-# Quoted symbol
-$pattern = MojoX::Routes::Pattern->new('/(:controller)test/(action)');
-$pattern->defaults({action => 'index'});
-$result = $pattern->match('/footest/bar');
-is $result->{controller}, 'foo', 'right value';
-is $result->{action},     'bar', 'right value';
-is $pattern->render({controller => 'zzz', action => 'lala'}), '/zzztest/lala',
-  'right result';
-$result = $pattern->match('/test/lala');
-is $result, undef, 'no result';
-
-# Format
-$pattern = MojoX::Routes::Pattern->new('/(controller)test/(action)');
-is $pattern->format, undef, 'no value';
-$pattern = MojoX::Routes::Pattern->new('/(:controller)test/:action.html');
-is $pattern->format, 'html', 'right value';
-$pattern = MojoX::Routes::Pattern->new('/index.cgi');
-is $pattern->format, 'cgi', 'right value';
-
-# Relaxed
-$pattern = MojoX::Routes::Pattern->new('/test/(.controller)/:action');
-$result  = $pattern->match('/test/foo.bar/baz');
-is $result->{controller}, 'foo.bar', 'right value';
-is $result->{action},     'baz',     'right value';
-is $pattern->render({controller => 'foo.bar', action => 'baz'}),
-  '/test/foo.bar/baz', 'right result';
-$pattern = MojoX::Routes::Pattern->new('/test/(.groovy)');
-$result  = $pattern->match('/test/foo.bar');
-is $pattern->format, undef, 'no value';
-is $result->{groovy}, 'foo.bar', 'right value';
-is $result->{format}, undef,     'no value';
-is $pattern->render({groovy => 'foo.bar'}), '/test/foo.bar', 'right result';
-
-# Wildcard
-$pattern = MojoX::Routes::Pattern->new('/test/(:controller)/(*action)');
-$result  = $pattern->match('/test/foo/bar.baz/yada');
-is $result->{controller}, 'foo',          'right value';
-is $result->{action},     'bar.baz/yada', 'right value';
-is $pattern->render({controller => 'foo', action => 'bar.baz/yada'}),
-  '/test/foo/bar.baz/yada', 'right result';
-
-# Render false value
-$pattern = MojoX::Routes::Pattern->new('/:id');
-is $pattern->render({id => 0}), '/0', 'right result';
-
-# Regex in path
-$pattern = MojoX::Routes::Pattern->new('/:test');
-$result  = $pattern->match('/test(test)(\Qtest\E)(');
-is $result->{test}, 'test(test)(\Qtest\E)(', 'right value';
-is $pattern->render({test => '23'}), '/23', 'right result';
@@ -1,47 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use Test::More tests => 5;
-
-use Mojo;
-use MojoX::Dispatcher::Routes::Controller;
-use MojoX::Renderer;
-
-# Actually, she wasn't really my girlfriend,
-# she just lived nextdoor and never closed her curtains.
-my $c = MojoX::Dispatcher::Routes::Controller->new(app => Mojo->new);
-$c->app->log->path(undef);
-$c->app->log->level('fatal');
-my $r = MojoX::Renderer->new(default_format => 'debug');
-$r->add_handler(
-    debug => sub {
-        my ($self, $c, $output) = @_;
-        $$output .= 'Hello Mojo!';
-    }
-);
-$c->stash->{format} = 'something';
-
-# Normal rendering
-$c->stash->{template} = 'something';
-$c->stash->{handler}  = 'debug';
-is_deeply [$r->render($c)], ['Hello Mojo!', 'text/plain'], 'normal rendering';
-
-# Normal rendering with layout
-$c->stash->{template} = 'something';
-$c->stash->{layout}   = 'something';
-$c->stash->{handler}  = 'debug';
-is_deeply [$r->render($c)], ['Hello Mojo!Hello Mojo!', 'text/plain'],
-  'normal rendering with layout';
-is delete $c->stash->{layout}, 'something';
-
-# Rendering a path with dots
-$c->stash->{template} = 'some.path.with.dots/template';
-$c->stash->{handler}  = 'debug';
-is_deeply [$r->render($c)], ['Hello Mojo!', 'text/plain'],
-  'rendering a path with dots';
-
-# Unrecognized handler
-$c->stash->{handler} = 'not_defined';
-is $r->render($c), undef, 'return undef for unrecognized handler';
@@ -1,515 +0,0 @@
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-use Test::More tests => 205;
-
-use Mojo::Transaction::HTTP;
-
-# They're not very heavy, but you don't hear me not complaining.
-use_ok 'MojoX::Routes';
-use_ok 'MojoX::Routes::Match';
-
-# Routes
-my $r = MojoX::Routes->new;
-
-# /clean
-$r->route('/clean')->to(clean => 1);
-
-# /clean/too
-$r->route('/clean/too')->to(something => 1);
-
-# /*/test
-my $test = $r->route('/:controller/test')->to(action => 'test');
-
-# /*/test/edit
-$test->route('/edit')->to(action => 'edit')->name('test_edit');
-
-# /*/test/delete/*
-$test->route('/delete/(id)', id => qr/\d+/)->to(action => 'delete', id => 23);
-
-# /test2
-my $test2 = $r->bridge('/test2')->to(controller => 'test2');
-
-# /test2 (inline)
-my $test4 = $test2->bridge->to(controller => 'index');
-
-# /test2/foo
-$test4->route('/foo')->to(controller => 'baz');
-
-# /test2/bar
-$test4->route('/bar')->to(controller => 'lalala');
-
-# /test2/baz
-$test2->route('/baz')->to('just#works');
-
-# /test3
-my $test3 = $r->waypoint('/test3')->to(controller => 's', action => 'l');
-
-# /test3/edit
-$test3->route('/edit')->to(action => 'edit');
-
-# /
-$r->route('/')->to(controller => 'hello', action => 'world');
-
-# /wildcards/1/*
-$r->route('/wildcards/1/(*wildcard)', wildcard => qr/(.*)/)
-  ->to(controller => 'wild', action => 'card');
-
-# /wildcards/2/*
-$r->route('/wildcards/2/(*wildcard)')
-  ->to(controller => 'card', action => 'wild');
-
-# /wildcards/3/*/foo
-$r->route('/wildcards/3/(*wildcard)/foo')
-  ->to(controller => 'very', action => 'dangerous');
-
-# /format
-# /format.html
-$r->route('/format')
-  ->to(controller => 'hello', action => 'you', format => 'html');
-
-# /format2.html
-$r->route('/format2.html')->to(controller => 'you', action => 'hello');
-
-# /format2.json
-$r->route('/format2.json')->to(controller => 'you', action => 'hello_json');
-
-# /format3/*.html
-$r->route('/format3/:foo.html')->to(controller => 'me', action => 'bye');
-
-# /format3/*.json
-$r->route('/format3/:foo.json')->to(controller => 'me', action => 'bye_json');
-
-# /articles
-# /articles.html
-# /articles/1
-# /articles/1.html
-# /articles/1/edit
-# /articles/1/delete
-my $articles = $r->waypoint('/articles')->to(
-    controller => 'articles',
-    action     => 'index',
-    format     => 'html'
-);
-my $wp = $articles->waypoint('/:id')->to(
-    controller => 'articles',
-    action     => 'load',
-    format     => 'html'
-);
-my $bridge = $wp->bridge->to(
-    controller => 'articles',
-    action     => 'load',
-    format     => 'html'
-);
-$bridge->route('/edit')->to(controller => 'articles', action => 'edit');
-$bridge->route('/delete')->to(
-    controller => 'articles',
-    action     => 'delete',
-    format     => undef
-)->name('articles_delete');
-
-# GET /method/get
-$r->route('/method/get')->via('GET')
-  ->to(controller => 'method', action => 'get');
-
-# POST /method/post
-$r->route('/method/post')->via('post')
-  ->to(controller => 'method', action => 'post');
-
-# POST|GET /method/post_get
-$r->route('/method/post_get')->via(qw/POST get/)
-  ->to(controller => 'method', action => 'post_get');
-
-# /simple/form
-$r->route('/simple/form')->to('test-test#test');
-
-# /edge/gift
-my $edge = $r->route('/edge');
-my $auth = $edge->bridge('/auth')->to('auth#check');
-$auth->route('/about/')->to('pref#about');
-$auth->bridge->to('album#allow')->route('/album/create/')->to('album#create');
-$auth->route('/gift/')->to('gift#index')->name('gift');
-
-# Make sure stash stays clean
-my $tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/clean');
-my $m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{clean},     1,     'right value';
-is $m->stack->[0]->{something}, undef, 'no value';
-is $m->url_for, '/clean', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/clean/too');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{clean},     undef, 'no value';
-is $m->stack->[0]->{something}, 1,     'right value';
-is $m->url_for, '/clean/too', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Real world example using most features at once
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/articles.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'articles', 'right value';
-is $m->stack->[0]->{action},     'index',    'right value';
-is $m->stack->[0]->{format},     'html',     'right value';
-is $m->url_for, '/articles', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/articles/1.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'articles', 'right value';
-is $m->stack->[0]->{action},     'load',     'right value';
-is $m->stack->[0]->{id},         '1',        'right value';
-is $m->stack->[0]->{format},     'html',     'right value';
-is $m->url_for(format => 'html'), '/articles/1.html', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/articles/1/edit');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[1]->{controller}, 'articles', 'right value';
-is $m->stack->[1]->{action},     'edit',     'right value';
-is $m->stack->[1]->{format},     'html',     'right value';
-is $m->url_for, '/articles/1/edit', 'right URL';
-is $m->url_for(format => 'html'), '/articles/1/edit.html', 'right URL';
-is $m->url_for('articles_delete', format => undef), '/articles/delete',
-  'right URL';
-is $m->url_for('articles_delete'), '/articles/delete', 'right URL';
-is $m->url_for('articles_delete', id => 12), '/articles/12/delete',
-  'right URL';
-is @{$m->stack}, 2, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/articles/1/delete');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[1]->{controller}, 'articles', 'right value';
-is $m->stack->[1]->{action},     'delete',   'right value';
-is $m->stack->[1]->{format},     undef,      'no value';
-is $m->url_for, '/articles/1/delete', 'right URL';
-is @{$m->stack}, 2, 'right number of elements';
-
-# Root
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->captures->{controller}, 'hello', 'right value';
-is $m->captures->{action},     'world', 'right value';
-is $m->stack->[0]->{controller}, 'hello', 'right value';
-is $m->stack->[0]->{action},     'world', 'right value';
-is $m->url_for, '/', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Path and captures
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/foo/test/edit');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->captures->{controller}, 'foo',  'right value';
-is $m->captures->{action},     'edit', 'right value';
-is $m->stack->[0]->{controller}, 'foo',  'right value';
-is $m->stack->[0]->{action},     'edit', 'right value';
-is $m->url_for, '/foo/test/edit', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Optional captures in sub route with requirement
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/bar/test/delete/22');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->captures->{controller}, 'bar',    'right value';
-is $m->captures->{action},     'delete', 'right value';
-is $m->captures->{id},         22,       'right value';
-is $m->stack->[0]->{controller}, 'bar',    'right value';
-is $m->stack->[0]->{action},     'delete', 'right value';
-is $m->stack->[0]->{id},         22,       'right value';
-is $m->url_for, '/bar/test/delete/22', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Defaults in sub route
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/bar/test/delete');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->captures->{controller}, 'bar',    'right value';
-is $m->captures->{action},     'delete', 'right value';
-is $m->captures->{id},         23,       'right value';
-is $m->stack->[0]->{controller}, 'bar',    'right value';
-is $m->stack->[0]->{action},     'delete', 'right value';
-is $m->stack->[0]->{id},         23,       'right value';
-is $m->url_for, '/bar/test/delete', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Chained routes
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test2/foo');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'test2', 'right value';
-is $m->stack->[1]->{controller}, 'index', 'right value';
-is $m->stack->[2]->{controller}, 'baz',   'right value';
-is $m->captures->{controller}, 'baz', 'right value';
-is $m->url_for, '/test2/foo', 'right URL';
-is @{$m->stack}, 3, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test2/bar');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'test2',  'right value';
-is $m->stack->[1]->{controller}, 'index',  'right value';
-is $m->stack->[2]->{controller}, 'lalala', 'right value';
-is $m->captures->{controller}, 'lalala', 'right value';
-is $m->url_for, '/test2/bar', 'right URL';
-is @{$m->stack}, 3, 'right number of elements';
-$tx->req->url->parse('/test2/baz');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'test2', 'right value';
-is $m->stack->[1]->{controller}, 'just',  'right value';
-is $m->stack->[1]->{action},     'works', 'right value';
-is $m->stack->[2], undef, 'no value';
-is $m->captures->{controller}, 'just', 'right value';
-is $m->url_for, '/test2/baz', 'right URL';
-is @{$m->stack}, 2, 'right number of elements';
-
-# Waypoints
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test3');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 's', 'right value';
-is $m->stack->[0]->{action},     'l', 'right value';
-is $m->url_for, '/test3', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test3/');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 's', 'right value';
-is $m->stack->[0]->{action},     'l', 'right value';
-is $m->url_for, '/test3', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test3/edit');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 's',    'right value';
-is $m->stack->[0]->{action},     'edit', 'right value';
-is $m->url_for, '/test3/edit', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Named url_for
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/test3');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->url_for, '/test3', 'right URL';
-is $m->url_for('test_edit', controller => 'foo'), '/foo/test/edit',
-  'right URL';
-is $m->url_for('test_edit', {controller => 'foo'}), '/foo/test/edit',
-  'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Wildcards
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/wildcards/1/hello/there');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'wild',        'right value';
-is $m->stack->[0]->{action},     'card',        'right value';
-is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
-is $m->url_for, '/wildcards/1/hello/there', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/wildcards/2/hello/there');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'card',        'right value';
-is $m->stack->[0]->{action},     'wild',        'right value';
-is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
-is $m->url_for, '/wildcards/2/hello/there', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/wildcards/3/hello/there/foo');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'very',        'right value';
-is $m->stack->[0]->{action},     'dangerous',   'right value';
-is $m->stack->[0]->{wildcard},   'hello/there', 'right value';
-is $m->url_for, '/wildcards/3/hello/there/foo', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Escaped
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/wildcards/1/http://www.google.com');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'wild',                  'right value';
-is $m->stack->[0]->{action},     'card',                  'right value';
-is $m->stack->[0]->{wildcard},   'http://www.google.com', 'right value';
-is $m->url_for, '/wildcards/1/http://www.google.com', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/wildcards/1/http%3A%2F%2Fwww.google.com');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'wild',                  'right value';
-is $m->stack->[0]->{action},     'card',                  'right value';
-is $m->stack->[0]->{wildcard},   'http://www.google.com', 'right value';
-is $m->url_for, '/wildcards/1/http://www.google.com', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Format
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'hello', 'right value';
-is $m->stack->[0]->{action},     'you',   'right value';
-is $m->stack->[0]->{format},     'html',  'right value';
-is $m->url_for, '/format', 'right URL';
-is $m->url_for(format => undef),  '/format',      'right URL';
-is $m->url_for(format => 'html'), '/format.html', 'right URL';
-is $m->url_for(format => 'txt'),  '/format.txt',  'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'hello', 'right value';
-is $m->stack->[0]->{action},     'you',   'right value';
-is $m->stack->[0]->{format},     'html',  'right value';
-is $m->url_for, '/format', 'right URL';
-is $m->url_for(format => undef),  '/format',      'right URL';
-is $m->url_for(format => 'html'), '/format.html', 'right URL';
-is $m->url_for(format => 'txt'),  '/format.txt',  'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format2.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'you',   'right value';
-is $m->stack->[0]->{action},     'hello', 'right value';
-is $m->stack->[0]->{format},     'html',  'right value';
-is $m->url_for, '/format2.html', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format2.json');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'you',        'right value';
-is $m->stack->[0]->{action},     'hello_json', 'right value';
-is $m->stack->[0]->{format},     'json',       'right value';
-is $m->url_for, '/format2.json', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format3/baz.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'me',   'right value';
-is $m->stack->[0]->{action},     'bye',  'right value';
-is $m->stack->[0]->{format},     'html', 'right value';
-is $m->stack->[0]->{foo},        'baz',  'right value';
-is $m->url_for, '/format3/baz.html', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/format3/baz.json');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'me',       'right value';
-is $m->stack->[0]->{action},     'bye_json', 'right value';
-is $m->stack->[0]->{format},     'json',     'right value';
-is $m->stack->[0]->{foo},        'baz',      'right value';
-is $m->url_for, '/format3/baz.json', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Request methods
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/method/get.html');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'method', 'right value';
-is $m->stack->[0]->{action},     'get',    'right value';
-is $m->stack->[0]->{format},     'html',   'right value';
-is $m->url_for, '/method/get', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('POST');
-$tx->req->url->parse('/method/post');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'method', 'right value';
-is $m->stack->[0]->{action},     'post',   'right value';
-is $m->stack->[0]->{format},     undef,    'no value';
-is $m->url_for, '/method/post', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/method/post_get');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'method',   'right value';
-is $m->stack->[0]->{action},     'post_get', 'right value';
-is $m->stack->[0]->{format},     undef,      'no value';
-is $m->url_for, '/method/post_get', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('POST');
-$tx->req->url->parse('/method/post_get');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'method',   'right value';
-is $m->stack->[0]->{action},     'post_get', 'right value';
-is $m->stack->[0]->{format},     undef,      'no value';
-is $m->url_for, '/method/post_get', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('DELETE');
-$tx->req->url->parse('/method/post_get');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, undef, 'no value';
-is $m->stack->[0]->{action},     undef, 'no value';
-is $m->stack->[0]->{format},     undef, 'no value';
-is $m->url_for, '', 'no URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Not found
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/not_found');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->url_for('test_edit', controller => 'foo'), '/foo/test/edit',
-  'right URL';
-is @{$m->stack}, 0, 'no elements';
-
-# Simplified form
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/simple/form');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'test-test', 'right value';
-is $m->stack->[0]->{action},     'test',      'right value';
-is $m->stack->[0]->{format},     undef,       'no value';
-is $m->url_for, '/simple/form', 'right URL';
-is $m->url_for('current'), '/simple/form', 'right URL';
-is @{$m->stack}, 1, 'right number of elements';
-
-# Special edge case with nested bridges
-$tx = Mojo::Transaction::HTTP->new;
-$tx->req->method('GET');
-$tx->req->url->parse('/edge/auth/gift');
-$m = MojoX::Routes::Match->new($tx)->match($r);
-is $m->stack->[0]->{controller}, 'auth',  'right value';
-is $m->stack->[0]->{action},     'check', 'right value';
-is $m->stack->[0]->{format},     undef,   'no value';
-is $m->stack->[1]->{controller}, 'gift',  'right value';
-is $m->stack->[1]->{action},     'index', 'right value';
-is $m->stack->[1]->{format},     undef,   'no value';
-is $m->stack->[2], undef, 'no value';
-is $m->url_for, '/edge/auth/gift', 'right URL';
-is $m->url_for('gift'),    '/edge/auth/gift', 'right URL';
-is $m->url_for('current'), '/edge/auth/gift', 'right URL';
-is @{$m->stack}, 2, 'right number of elements';